【算法初探】前端学算法之旋转数组(1)

157 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

现在市面上前端面试考察到算法的公司越来越多,并不是因为做前端的人多了,所以面试越来越卷,恰恰说明了前端能做的工作也越来越多。

最初算法只有后端人员面试时才会考察,而现在前端也需要算法,足以说明这一点。一个懂算法的前端与不懂算法的前端,他们的思维方式及代码的编写都可能会大大的不同,因此作为一个有追求的前端,我们还是需要好好的学习一下算法,哪怕只是为了在面试中让自己更加的出彩也是值得的。

虽然大家都知道算法是加分项,但是很多人对算法'望而生畏',一遇到算法题就打退堂鼓,总觉得算法很难,其实我们只要掌握了相应的方法,那么算法学起来其实并没有那么困难。

今天我们就先从一道简单的算法题开始,让我们一步一步走入算法的怀抱。因为是第一道题,所以我们用初探这个词来形容,高手可以略过。

这是在leetcode上的第189道题,具体的提纲描述可以看这里,大致如下:

给你一个数组,将数组中的元素向右轮转 `k` 个位置,其中 `k` 是非负数。

简单描述如下:
1. 输入一个数组 [1, 2, 3, 4, 5, 6, 7]
2. k = 2, 即旋转 23. 输出 [6, 7, 1, 2, 3, 4, 5]

旋转数组的意思就是,将数组中的最后一位往前移动,移动的次数就是这个k值,因此第一步的我们需要将数组中的7移动到第一位,我们得到的结果就是[7, 1, 2, 3, 4, 5, 6],然后因为k值是2,所以我们还需要往前再移动一次,因此最终得到的结果就是上面看到的[6, 7, 1, 2, 3, 4, 5],这里只是大概描述这个题目的意思,那么接下来我们需要开始分析如何编写相关的代码,并得到最终我们需要的结果。

思路分析

首先当我们拿到这个题目的时候,我们第一时间应该想到的就是数组的基本操作,如果对数组的基本操作还不熟悉的童鞋,可以看这里

下面我们提供两种结题思路:

思路一:首先通过pop把数组末尾的元素挨个弹出,然后通过unshift将弹出的内容添加到数组的前面;

思路二:将数组进行拆分,然后通过concat将拆分出来的数组拼接到一起。

上面我们只是通过文字将简单的思路描述了一下,那么接下来我们开始代码的实战演练,这里我使用ts来进行编写,还有对ts不了解的童鞋,可以在掘金上搜一下ts相关的文章,相信会让你很快上手,并彻底爱上它的。

代码演练

思路一实现,代码如下:

/*
 * 旋转数组 K 步,使用 pop 和 unshift
 * @param arr number[]
 * @param k number
 * @returns arr number[]
 */
const rotate1 = (arr: number[], k: number): number[] => {
    // 获取数组的长度
    const len = arr.length;
    // 如果旋转的k值不存在,或者数组的长度为0,则直接返回该数组
    if (!k || len === 0) return arr;
    // 获取余数的绝对值,防止k大于数组的长度
    const step = Math.abs(k % len);
    // 时间复杂度 O(n^2)
    for (let i = 0; i < step; i++) {
        const last = arr.pop();
        if (last != null) {
            arr.unshift(last); // 数据是有序结构,unshift 操作数字非常慢!O(n)
        }
    }
    return arr;
}

// 测试
const arr = [1, 2, 3, 4, 5, 6, 7];
const arr1 = rotate1(arr, 2);
console.log(arr1);  // [6, 7, 1, 2, 3, 4, 5]

思路二实现,代码如下:

/*
 * 旋转数组 K 步,使用 concat
 * @param arr number[]
 * @param k number
 * @returns arr number[]
 */
const rotate2 = (arr: number[], k: number): number[] => {
    // 获取数组的长度
    const len = arr.length;
    // 如果旋转的k值不存在,或者数组的长度为0,则直接返回该数组
    if (!k || len === 0) return arr;
    // 获取余数的绝对值,防止k大于数组的长度
    const step = Math.abs(k % len);
    // 取出数组最后的内容,负的值是从数组最后面开始截取
    const part1 = arr.slice(-step);
    // 从第0位开始,截取剩余的长度,因为上一步已经把数据某位的数字取出来了,所以这里只需要取还剩余的内容即可
    const part2 = arr.slice(0, len - step);
    // 合并数组
    const part3 = part1.concat(part2);
    return part3;
}

// 测试
const arr = [1, 2, 3, 4, 5, 6, 7];
const arr2 = rotate2(arr, 2);
console.log(arr2);  // [6, 7, 1, 2, 3, 4, 5]

思考一下🤔,虽然我们在上面通过两种方式实现了旋转数组,并最终得到了我们想要的答案,但是这两种实现方法到底哪种方式更优呢?下一节我们在一起来分析。

结尾

这里我们虽然给出了相应的解答方式,但是我们还没有给大家分析出这两种实现方式的差异,这一节就先到这里了,下一节我们再一起来分析这两种解题方式的差异性,如果你也是个算法小白,那么我们一起来学习把,相信自己的付出一定会有回报的,感谢大家的支持。

如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家

参考文献

2周刷完100道前端优质面试真题