Remove Duplicates from Sorted Array
已知:给定一个按照升序排列好的数组,该数组中的元素有些是重复出现的。
所求:将数组中重复的元素删除,所有的元素仅出现一次。
要求:不要申请新的空间,在固定数量的内存空间上设计算法。LeetCode中的in place表示原地,现场的意思,表示不再使用A之外的内存。
例子:
给定数组 A = [1, 1, 2]
返回新数组长度2,此时A = [1, 2]。
提示:Array, Two Pointers
思路:
1、数组A在算法执行的过程中从前向后重新赋值,假设重新赋值后的数组记为A',A'随着算法的进行不断扩展。A和A'共用一段内存空间。
2、在一个for循环中使用两个下标i和j。i表示当前处理的数组A中的元素下标,j表示数组A'的最后一个元素下标。可以理解为属于A,j属于A’。
3、使用for循环对A中的元素逐一遍历,当i所在的元素与j所在的元素不相等时,说明A'中没有A[i]元素,此时将i所在的元素A[i]插入到j+1的位置上。A'得到扩展。
代码:
class Solution {
public:
int removeDuplicates(int A[], int n) {
if( n == 0 )
return 0;
int j = 0;
for( int i = 1; i < n; i++ ){
if( A[i] != A[j] ){
A[++j] = A[i];
}
}
return j+1;
}
};
代码分析:
第4行和第5行处理了当传入的数组A为空时的特殊情况。
第8行开始的for循环到第12行结束,该for循环的循环变量为i,通过i依次访问数组A当中的每一个元素。第7行定义的j在第10行使用,同时在for循环内部不断变化。变量i和变量j是数组A的两个下标,LeetCode提示中的Two Pointers在此变形为i和j这两个下标。
算法会在执行的过程中从前向后不断改变A中的元素,假设被改变后的数组记为A',循环变量j就是A'的最后一个元素。而A'就是所求的数组。
for循环中只针对A[i] != A[j]的情况作了分支。表示循环会找到第一个与A[j]不同的元素的位置,该元素对于A'是一个新的不重复的元素,于是A'被扩展。A'被扩展表现在第10行,下标增加1,然后在新的最末位置插入不重复的元素A[i]。for循环继续执行,找到与A'数组最末一个元素不同的元素,然后加入到A'数组中。最后得到的A'数组一定包含了A中的所有元素,并且保证每个元素仅出现一次。
在这里需要注意,A'其实与A指向同一组内存空间,A'的长度小于或等于A。因此,算法满足题目的要求(不申请额外的空间,使用常数个存储空间)。
Remove Duplicates from Sorted Array II
已知:给定一个按照升序排列好的数组,该数组中的元素有些是重复出现的。
所求:删除部分重复元素,使得数组中重复次数大于或等于2的元素仅重复两次,出现1次的元素仍然出现1次。
要求:不要申请新的空间,在固定数量的内存空间上设计算法。
例子:
给定数组 A = [1, 1, 1, 2, 2, 3]
返回新数组长度5,此时A = [1, 1, 2, 2, 3]。
提示:Array, Two Pointers
加入一个bool变量,在循环的过程中判断第i个元素是不是第一个与第j个元素相同的元素,如果是,则加入到A'中;如果不是,则不加入A'。
代码:
class Solution {
public:
int removeDuplicates(int A[], int n) {
if(n==0) return 0;
int j = 0;
bool moveSecondEle = true;
for(int i = 1; i < n; i++) {
if(moveSecondEle && A[j]==A[i]){
A[++j] = A[i];
moveSecondEle = false;
}else if(A[j]!=A[i]) {
A[++j] = A[i];
moveSecondEle = true;
}
}
return j+1;
}
};
代码分析:
第7行在for循环开始前定义了一个布尔变量moveSecondEle,这个变量用来判断位置i上的元素是否是第1个与位置j上的元素相等的元素。moveSecondEle初始为true,当第一次出现A[j]等于A[i]时,将A[i]加入到A'中,加入的元素是A[j]在A’中第二次出现,故我使用了moveSecondEle这个变量名。之后,moveSecondEle变为false,在遇到与A[j]相同的元素也无法加入到A'中。
这个方法可以拓展,将moveSecondEle声明为int型,就可以使得A‘中的可重复元素变为3、4或者其他。