一、概述
给一个有序的vector,删除其中的重复元素。要求空间复杂度为O(1),不允许另外新建vector。
我第一反应就是用vector自带的erase函数。但是效果很差。
然后开始思考,我这种方法没有用到有序这一特性,只用到了“相同元素全在一起”这个特性。也就是说,对于如下两个序列:
0111222333445555566
和
8888885555666111222
我这种算法的效率是一样的。
为了用到有序这一特性,使用赋值操作可以更快。
完美,时间缩短了十倍。
二、分析
1、我的代码一
很简单,从头到尾遍历vector,如果当前元素的值与下一个元素的值相等,则erase当前元素。但要注意erase的应用。
erase函数有两种调用方式:
第一种直接传入一个iterator,那么就将这个iterator对应的元素删除,同时删除这个iterator,返回它的下一个。
第二种则是传入(begin+n),那么就删除第n个元素。
注意,在使用第一种的时候,传入的不能是循环中使用的iterator,否则在erase之后+1时候会报错。你都erase了,变空了,还怎么+1。
在使用第二种的时候,注意每次erase后,vector的长度会变化(这不是废话么)。因此删除后的begin+n其实指向的是删除前的begin+n+1。因此删除后要执行n-1。
代码如下:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.size()==0)
return 0;
if(nums.size()==1)
{
return 1;
}
for(int i=0;i<nums.size()-1;i++)
{
if(nums[i]==nums[i+1])
{
nums.erase(nums.begin()+i);
i--;
}
}
return nums.size();
}
};
2、我的代码二
代码二不使用erase,erase太耗费资源了。你想啊,vector就是链表,删除链表中的一个元素多麻烦。不如直接把后面元素放在前面。那么问题来了,我想把后面的元素x放到前面,怎么保证前面没有x呢?
因为vector有序啊。如果x大于前面的最大的元素,那么肯定前面没有x了。
这就是整体思路。
需要维护两个指针。第一个a指向“前面最大的元素”,也就是“最后保留的元素序列中当前的最后一个”。第二个b指向遍历过程中的当前元素。每当b指向的元素大于a,那么就将a后面的元素赋值为b,然后a向后移动一个,b向后移动一个即可。
最后vector前面的n个元素就是从小到大排列的n个不同元素。那如何删除后面的所有元素呢?
resize(n)即可。该函数当n<size()时,截断vector到第n个,n>size()时,在后面填充直到n。
代码如下:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.size()==0)
return 0;
if(nums.size()==1)
{
return 1;
}
int now=0;
for(int i=0;i<nums.size();i++)
{
if(nums[now]<nums[i])
{
nums[now+1]=nums[i];
now=now+1;
}
}
nums.resize(now+1);
return nums.size();
}
};
三、总结
当算法的时间复杂度不理想的时候,要考虑以下,我用的算法是否已经用到了题目的所有条件。是不是减少某一个条件算法仍成立?如果仍成立,那么突破点就在这个条件上。类似本题中的有序条件。