算法笔记3:删除重复项

题目

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k 。

示例 1:输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 不需要考虑数组中超出新长度后面的元素。

示例 2:输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

题解

方法一:双指针

双指针果然是个好东西,在解决数组中的问题时可谓能够大杀四方。对于这一道题,我们只需要设置左右两个指针使他们一快一慢的前进遍历整个数组即可。不妨将移动快的指针称作fast,慢的那个称作slow,首先来看看代码:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n = nums.size();
        if (n == 0) {
            return 0;
        }
        int fast = 1, slow = 1;
        while (fast < n) {
            if (nums[fast] != nums[fast - 1]) {
                nums[slow] = nums[fast];
                ++slow;
            }
            ++fast;
        }
        return slow;
    }
};

在这段代码中,我们首先获取了nums数组的大小,为了避免数组为空的情况影响我们代码的正常运行,我们首先判断n为0时,直接退出代码运行,返回值为0。代码的健壮性也是至关重要的一环。接下来,我们请出两位指针先生站在起跑线上。为了方便,我们不需要从第0号元素开始遍历,因为无论如何,第一个元素都是会被保留下来的。接下来进入一个while循环,条件为fast指针没有越界,也就是遍历到数组大小的上限时停下来。接着我们进行判断,如果右指针fast与其左边的一位元素不相等,就使左指针slow记录下fast所指向的值,同时,左指针向前迈一步。如若不满足该if语句的判定条件,即,右指针fast的元素与其左边的元素是一样的,这时候就需要我们去除相同元素,那我们就对左指针不做处理,使右指针继续向下一位扫描。最终,右指针遍历越界,返回左指针的大小值,即为所求。

这就相当于,我们的fast指针是一个身先士卒的长官,向前冲锋时总是走在最前方,而slow指针是支援部队,每当fast指针看见相同的情况,就不发出信号,slow按兵不动;如若前方有所变化,fast就告诉后方,slow就记下变化并且向前跟进一步。最终slow将之前的战场进行了覆盖,留下了最新的地形图。

本题目的空间复杂度很小,因为我们又创建了一个原地算法(点开题目中的链接就能查询),而时间复杂度也仅仅是遍历一遍数组,所以各项性能已经十分的完善与优化。可喜可贺,这次我们一下就写出了最棒的算法。

方法二:开挂

难道还有更优解?答案是否定的,但是不妨碍我们了解更多库与工具。直接看代码:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        return distance(nums.begin(), unique(nums.begin(), nums.end()));
    }
};

一行就完成了我们的需求与任务。这是使用了algorithm库中的函数,严格意义上来讲,这样做算法题已经失去了意义。但是无妨,我们了解更多的工具总是好的。这里使用了unique函数去取得nums中的每一个唯一值,然后使用了distance函数整理了整个数组。在实际运用中,是一种快捷简便的操作方法。

写在最后

通过之前对数组的练习,我们轻而易举的就拿出了双指针秒杀了这道题,但是对于细节方面,如,数组的边界、代码的健壮性等问题还需要我们细致的考虑。双指针可真是个大杀器。

Breeze Wang

A student majoring in Software Engineering at Central South University has an understanding of software development techniques, software architecture, and is able to use Godot to develop game projects. I am currently in the Game Development Laboratory at Central South University. I have experience participating in Global Game Jam. Loving game development.