LeetCode刷题系列---26. 删除排序数组中的重复项

一、题目

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

做题可以直接到删除排序数组中的重复项

示例

输入

[1,1,2]

输出

2

解释

原数组的前两个元素被修改为 1, 2

二、解题

LeetCode只需要提交核心函数部分,输入输出都已经写好了。所以要明确给定函数的输入和输出。本题中函数的参数是一个vector,要了解vector的基本操作。
我在做题时会自己在本地练习,然后把核心函数按照LeetCode给定的函数做对应修改,所以我会给出两版代码,一个是我在本地练习时的代码(完整,可以运行),一个是我提交的程序。

vector的基本操作

vector的声明vector<int> v;
vector的插入v.push_back(a)(a是一个int类型的变量)
获取vector中的元素个数v.size()
获取vector中第i个元素v[i]
以上参考数组长度的获得 vector的用法

解题思路1

因为数组是排好序的,所以如果存在重复项,一定是相邻项。所以,从第一个元素开始依次遍历数组元素,确认后一项的值是否与其相等,如果相等,则后边所有的项向前复制一步。

本地代码

先输入n,接下来输入n个有序且可能重复的整数,存入数组nums。
输出不重复的元素个数len,及删除重复元素后的数列。

#include <cstdio>

int n, nums[2000];

int removeDuplicates(){
	int len = n;
	for(int i=0; i<len-1; i++){
		if(nums[i+1]==nums[i]){
			for(int k=i+1; k<len-1; k++)
				nums[k] = nums[k+1];
			i--;	
			len--;
		}
	}
		
	return len;
}

int main(){
	scanf("%d", &n);
	for(int a=0; a<n; a++)
		scanf("%d", &nums[a]);
	
	int len = removeDuplicates();
	printf("%d\n", len);
	for(int a=0; a<len; a++)
		printf("%d ", nums[a]);
	printf("\n");
	
	return 0;
} 

提交代码

	int removeDuplicates(vector<int>& nums) {
        int len = nums.size();
	    for(int i=0; i<len-1; i++){
		    if(nums[i+1]==nums[i]){
			    for(int k=i+1; k<len-1; k++)
				    nums[k] = nums[k+1];
                i--;
                len--;
		    }
	    }
        
        return len;
    }

需要注意

  1. i --;
    遍历到第i个元素,发现第i+1个元素与第i个元素重复,i+1后边的元素前依次前移,相当于把i+1元素删除了,但是接下来原来的第i+2个位置(移动以后的第i+1个位置)也可能与第i个元素重复,所以依然需要从第i个元素开始遍历。而在for循环中一次循环结束i会加一,所以需要先减一,以保证下轮循环依然是从原来的i值开始。
  2. len--;
    len初始化为数组长度,每删除一个元素(后边的元素往前移动一次),len需要减一。
  3. 复制时
    从前往后,把第k+1个元素赋值到第k个位置,原来第k个位置的元素是重复值或者已经被往前复制了的值,即使被覆盖了也没有关系。
    【扩展】插入排序时,需要从后往前执行复制,第i个元素需要插入第a个位置完成排序,a到i-1位置的元素挨个后移一位,因为第i个元素已经被其后一位位置保存,第i-1个元素复制到第i个位置时把第i个位置覆盖掉也没有关系。

解题思路2

i从0开始遍历,j从i+1开始。j一直后找,直到找到第一个与第i个元素不相等的元素,把第j个元素前移到第i+1的位置,之后的元素依次前移。此时数组中第i个元素是唯一的了。i向后移动一位,j变为i+1的位置,重复上述操作。如果相邻的两个元素不等,也就是nums[i] != nums[j]则i和j都向后移动一位。

本地代码

#include <cstdio>

int n, nums[2000];

int removeDuplicates(){
	int len = n;
	if(len<=1) return len;
	int i=0, j=1;
	while(i<len){
		while(j<len&&nums[j]==nums[i]) j++;
		if(j>i+1){
			int t=i+1, k=j;
			while(k<len)
				nums[t++] = nums[k++];
			len = len - (j-i-1);
			i++; j = i+1;
		}
		else{
			i++; j++;
		}
	}
		
	return len;
}

int main(){
	scanf("%d", &n);
	for(int a=0; a<n; a++)
		scanf("%d", &nums[a]);
	
	int len = removeDuplicates();
	printf("%d\n", len);
	for(int a=0; a<len; a++)
		printf("%d ", nums[a]);
	printf("\n");
	
	return 0;
} 

提交代码

int removeDuplicates(vector<int>& nums) {
    int len = nums.size();
	if(len<=1) return len;
	int i=0, j=1;
	while(i<len){
		while(j<len&&nums[j]==nums[i]) j++;
		if(j>i+1){
			int t=i+1, k=j;
			while(k<len)
				nums[t++] = nums[k++];
			len = len - (j-i-1);
			i++; j = i+1;
		}
		else{
			i++; j++;
		}
	}
    return len;
}

别人的题解

该题的题解中很多人提到__“双指针法”__,然后去学习了一下。
双指针法,即在解题时使用两个指针(游标)同向或者对向共同遍历数组。

【扩展】
双指针法适用的问题:

  1. 有序数组中找到两个加和等于特定值的元素;
  2. 两个有序链表的合并;
  3. 快速排序;
  4. 将数组中的奇数都放在左边,偶数都放在右边。
  5. 求单链表中的中间值。

参考

  1. 【算法总结–数组相关】双指针法的常见应用
  2. 算法入门:双指针法

看了别人的题解,对比自己的解题思路,发现我每次都会把数组中剩余的元素往前复制,导致我的算法复杂度是O(n^2),其实没有必要,只需要把不重复的那一个数往前复制就好了。
具体的讲解可以看这个——【双指针】删除重复项-带优化思路,很清晰。

再复制一份别人的超简洁解法供自己学(mo)习(bai)。

int removeDuplicates(vector<int>& nums) {
	if (nums.size() < 2) return nums.size();
	int j = 0;
	for (int i = 1; i < nums.size(); i++)
		if (nums[j] != nums[i]) nums[++j] = nums[i];
	return ++j;
}

来自评论区

三、写在最后

以下是废话。
今天开始刷题,并不知道习惯知难而退的我能坚持多久。
但愿自己能多做点题,写成一个系列。
写这个刷题记录主要希望自己能思考和总结,而不是提交了通过了这题就结束了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值