快慢指针法?

力扣题目:27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-element
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

主要想解释一下代码随想录中的“快慢指针法”

本质其实就是将旧数组中的元素放到新数组中去而已,本质的代码如下

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        // 快慢指针法的本质如下,我觉得叫新旧指针法更好理解
        // old_index遍历整个数组
        vector<int> new_nums;   // 存放结果数组
        for (int old_index = 0; old_index < nums.size(); old_index ++ )
        {
            if (nums[old_index] != val) 
            {
                new_nums.push_back(nums[old_index]);
            }
        }
        // 将新数组的值放回旧数组,这步不用在意,反正已经达不到题目要求的空间复杂的O(1)啦,就是AC一下,测试一下
        for (int i = 0;  i < new_nums.size(); i ++ ) nums[i] = new_nums[i];
        return new_nums.size();
    }
};

优化上述代码的空间

其实新数组(向量vector类型)new_nums没必要开,因为新数组的指针下标(好吧用了push_back,看不出指针下标了),那我再写个更好理解的代码如下:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        // 快慢指针法的本质如下,我觉得叫新旧指针法更好理解
        // old_index遍历整个数组
        int new_nums[110];   // 存放结果数组
        int new_index = 0;
        for (int old_index = 0; old_index < nums.size(); old_index ++ )
        {
            if (nums[old_index] != val) 
            {
                new_nums[new_index] = nums[old_index];
                new_index ++ ;  // 新数组的指针
            }
        }
        // 将新数组的值放回旧数组,这步不用在意,反正已经达不到题目要求的空间复杂的O(1)啦,就是AC一下,测试一下
        for (int i = 0;  i < new_index; i ++ ) nums[i] = new_nums[i];
        return new_index;
    }
};

这时候可以开始说了

注意看上一块代码,new_index指针指向的是结果数组,也就是去掉指定元素后的数组,old_index指针用于遍历旧数组,也就是原始数组。我们会发现,new_index指针移动起来不会比old_index指针快的,也就是new_index <= old_index, 为什么呢?因为old_index指针每查看一个旧数组的元素,就判断这个该不该放到新数组中去,也是查看一个判断一个,new_index最大也就是跟old_index一样大(此时情况:没有一个要删除的元素,旧数组当前已遍历到的元素全部丢到新数组中去)

综上,new_index既然不会比old_index大,那直接利用原始数组的空间即可,不用浪费空间开新数组了,旧数组已经被遍历过的位置是可以利用的啦。

代码如下:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        // 快慢指针法的本质如下,我觉得叫新旧指针法更好理解
        // old_index遍历整个数组
        int new_index = 0;
        for (int old_index = 0; old_index < nums.size(); old_index ++ )
        {
            if (nums[old_index] != val) 
            {
                nums[new_index] = nums[old_index];
                new_index ++ ;  // 新数组的指针
            }
        }
        return new_index;
    }
};

附:我自己的双指针思路

我的双指针是丢头和尾,头那边的指针从头开始往后走,碰到要删除的,丢到末尾去就行(也就是和尾指针的元素交换位置),并将尾指针前移一位,相比快慢指针法(也就是上一块代码),缺点:改变了元素的的相对位置。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int len = nums.size();
        
        // 与val相等的数与末尾数交换位置,并且长度减1
        // 1 2 2 3 1  val = 1, 1丢到最后一位,并且长度减1
        for (int i = 0; i < len; i ++ )
        {
            if (nums[i] == val)
            {
                swap(nums[i], nums[len - 1]);
                len -- ;  // 长度
                i -- ;    // 保持指针不动
            }
        }

        return len;
    }
};

你可能感兴趣的:(代码随想录,c++,算法)