今天做了LeetCode的习题26,题目的目的是将排好序的序列中的重复元素去掉。
这道题的目的就是在排好序的数组中删除重复元素。判断元素是否重复不会太难,因为数组是排好序的,难的是如何对重复元素进行操作。
最简单的思路就是每发现一个重复元素就将后面的元素全部前移,但这对于长的数据来说是一种灾难。
一定要找到更简单的方法解决此问题。
还要注意约束条件是只能使用线性空间解决此问题,其他想到的一些例如记录重复数据序号的方法也是不可行的。
但后来发现自己想多了,题目给的是vector,还不如数组呢,因为在确定自己代码应该没问题的时候报时间超限的问题。
代码如下:
#include<vector>
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
std::vector<int>::iterator it = nums.begin() ;
while (it!=nums.end())
{
if (*it==*(it+1))
{
nums.erase(it);
}
it++;
}
return nums.size();
}
};
看着很完美啊,但就是一直在报错。
后来查阅得知:
如果在程序中对vector中的元素做了修改,添加或者删除元素之后,迭代器就会失效,这时候就不要使用迭代器了。
想一下,如果把当前元素删掉了,那么当前元素就是之前的后一个元素,可我对每一个元素都进行了迭代器后移的操作,岂不是乱了套。
之后就没注意了,不知道如何处理了。
再继续查找资料,后来完美的解决了问题。
(查函数的头文件伤不起啊!!!!!)
代码如下:
int removeDuplicates(vector<int>& nums) {
nums.erase(unique(nums.begin(), nums.end()), nums.end());
return nums.size();
}
注意就两行,就两行,就两行!!!!
就这么简单。
unique函数会将所有重复的元素都放在队列的后面,并且返回指向第一个重复元素的迭代器。
erase的作用是删除从第一个重复元素开始到最后的函数,
最后直接返回vector的size。
感谢库函数。
之后又想,如果直接返回size而不修改vector时如何写,想了一下,代码如下:
std::vector<int>::iterator it = nums.begin();
int sub = 0;
while (it != nums.end()-1)//end指的是最后一位元素还是最后一位元素之后的元素不确定
{
if (*it == *(it + 1))
{
sub++;
}
it++;
}
return nums.size() - sub;
之后又查阅了其他方法,各种佩服啊。
1.反向思维。
先上代码:
int removeDuplicates(int A[], int n) {
if (n < 2) return n;
int id = 1;
for (int i = 1; i < n; ++i)
if (A[i] != A[i - 1]) A[id++] = A[i];
return id;
}
一开始对这个代码特别不了解,所以觉得方法是错的。后来还是仔细跑了一下,发现这个方法很精妙。
他的基本思路就是从第二个元素(序列号为1)开始,依次和之前的元素比较,如果发现相同了,注意,这个时候是不会去处理的。如果发现不同,则说明这个元素肯定是在答案中的。以id另成为一套计数,放入当前id处并且id++(最后返回的是id,总会比真正元素最后的序数大1,数组很显然是从0开始计数,所以返回的就是size)。这样说完应该还是不够具体,拿一个小例子进行说明。
有此数组:{1, 2, 2, 3, 3, 4}
i:1 2 2 3 4
id:1
此时为初始化,虽然都是之的数组A,但是是两个计数系统,一会就能发现差别。
i=1:
id=1,A[id]=A[i]=2
id=2
i: 1 2 2 3 4
id: 1 2
i=2:
A[i]=A[i-1],不进行操作
i=3:
id=2,A[id]=A[i]=3
id=3
i: 1 2 2 3 4
id: 1 2 3
这里就可以看出来算法的核心了。如果要是没有重复的,就是自己等于自己,是没有变化的。如果有重复的 ,就相当于将此元素向前移动重复元素个位置。
i=4:
id=3,A[id]=A[i]=4
id=4
i: 1 2 2 3 4
id: 1 2 3 4
也就是说,每一次循环下来,id都是比结果的最后一个元素的序号大1,即最后元素的大小。
应该说的够清楚了。总算明白了。
同样思路但是更好理解的做法如下:
int count = 0;
for(int i = 1; i < n; i++){
if(A[i] == A[i-1]) count++;
else A[i-count] = A[i];
}
return n-count;
其实在解决这个问题的时候考虑的就是数组前移
这些方法非核心就是将每次发现重复元素都要进行的前移一位变为总共为O(n)次的前移若干位。