快慢指针-从leetcode标题说起

我是天空里的一片云,偶尔投影在你的波心。 –偶然

前语

先来看一道算法题:27.移出元素

给你一个数组 nums 和一个值 val,你需求 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要运用额定的数组空间,你必须仅运用 O(1) 额定空间并 原地修改输入数组。元素的次序能够改动。你不需求考虑数组中超出新长度后边的元素。

示例

例如:

输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2] ,给定nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,4,0,3]

想必各位第一时间想到的是JavaScript的splice函数,运用 for 循环,顺次遍历数组,找到等于 val 的元素并删去,

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
const removeElement = function (nums, val) {
    let len = nums.length;
    for (let i = 0; i < len; i  ) {
        if (nums[i] === val) {
            nums.splice(i, 1);
            len--;
            i--;
        }
    }
    return len;
};

这样看,这道题这道题很简单是不是?直接运用了 JavaScript 的 API,简单直接

好了,现在祝贺你,喜提一个称号,API 调用工程师,请戴好帽子(可个玩笑

那么咱们再看看这样的履行速度呢?

快慢指针-从leetcode标题说起

Oops,好像并不理想,内存占用只高于27.04%,

实际上,咱们这样做,就失去了这道题的含义,这道题考察的是关于数组的理解:

从数组说起

咱们都知道,数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。数组是一片连续的内存空间(这里只评论狭义的数组,快数组慢数组不在评论规模),而且数组没有删去操作,如果想要删去一个元素,在数组内部是比较复杂的操作,需求将行将删去的下表的方位顺次掩盖,

比方,数组 a,b,c 对应的下标顺次是 1、2、3,如果咱们要删去元素a,那么就需求将 a 后边的元素顺次前移,由 a、b、c,对应下表 1、2、3,变为 b、c,对应下表 1、2、3,

了解了数组的删去操作,那么在这里,咱们是不是能够复原一下这个思路呢?

暴力算法

知道了解题思路,咱们是不是就能够开始着手处理问题了。

首要想到的是双层 for 循环的暴力解法,第一层 for 是找出需求删去的元素,第二层 for 是顺次将后边的元素向前移动,掩盖需求删去的方位。

// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
/**
 * 
 * @param nums
 * @param val
 * @returns {*}
 */
function removeElementFor(nums, val) {
    let size = nums.length;
    for (let i = 0; i < size; i  ) {
        if (nums[i] === val) { // 发现需求移除的元素,就将数组团体向前移动一位
            for (let j = i   1; j < size; j  ) {
                nums[j - 1] = nums[j];
            }
            i--; // 因为下标i今后的数值都向前移动了一位,所以i也向前移动一位
            size--; // 此刻数组的大小-1
        }
    }
    return size;
}

这种算法并不需求太深入的思考

双指针

那么咱们可否优化一下暴力解法,记录下当时需求删去的方位?

实际上,咱们能够用两个指针,分别为快慢指针,快指针用于遍历所有元素,慢指针用于记录下当时除去需求删去的元素的下标,

这里借用卡尔教师代码随想录的图:代码随想录,卡尔教师的算法讲解的不错,咱们能够去瞅瞅

上代码:

/**
 * @param {number[]} nums
 * @param val
 * @returns {number}
 */
const removeElement1 = (nums, val) => {
    let slow=0;
    for (let i = 0; i < nums.length; i  ) {
        if (nums[i] !== val) {
            nums[slow  ] = nums[i];
        }
    }
    return slow;
}

看一下履行成果:

快慢指针-从leetcode标题说起

能够看到,运用快慢指针后,代码履行时间以及代码履行的内存都有了巨大的提升。

总结

在了解数组删去原理的基础上,咱们能够运用双指针的思路来处理。顺便说一下,关于算法题,咱们尽量不要运用库函数,因为算法题本身就是关于咱们思维的一个学习历练进程,直接运用库函数,一行代码把原来需求处理的问题给搞定了,也就失去了是考的进程了。

链接

更多快慢指针标题

力扣(LeetCode)官网 – 全球极客挚爱的技能生长渠道

力扣(LeetCode)官网 – 全球极客挚爱的技能生长渠道