前言
学习需要一步一个脚印。
刷题是每个程序员的必经之路。但是单单停留在刷过之后就算了,或者不懂查看别人的代码之后那一小段时间的理解是远远不够的,这种短时的记忆带来的负面效果是极其巨大的,比如我刷了很多题了,但是每次遇到新的题目总觉得以前遇到过,但重新编写的话却望而止步。为了避免这种情况,我们需要真正的理解每道题后面的原理,及时做出总结,这样才能永久的记在我们脑海里。
感谢您百忙之中抽出时间阅读此文章,接下来我会根据文章标题给出LeetCode上的相应有关这类话题的题目,并做出详细解析,再次感谢您的阅读。
提示 : 希望您在阅读完之后先尝试思考之后在看解析,这样效果会更好一点
LeetCode26
题目要求是这样的:给你一个排序之后的数组,需要删除数组中重复的元素,且不能开辟新的数据。
也就是说空间复杂度为O(1),我们只能在给的数组上操作。
思路
我们可以使用双指针进行扫描,定义指针i = 0 指向数组开头,j = 1 指向i的下一位,如果第 j 个元素不等于就把 i + 1 位置的数替换成 j 位置的元素. i 和 j 就同时往后移一位,如果第 j 个元素等于第 i 个元素,说明遇到重复的了我们就把 j 往后移动直到遇到不重复为止
我们使用一组数据来模拟一下这个过程
arr = {0 , 1 , 1 , 2 , 3}
- 1: i = 0 , j = 1 ,进入循环 arr[ i ] = 0 , arr[ j ] = 1 两个元素不相等 i ++ , arr[ i ] = arr[ j ] , j++
- 2: i = 1 , j = 2 ,arr[ i ] = 1 , arr[ j ] = 1 两个元素相等 j ++
- 3: i = 1 , j = 3 ,arr[ i ] = 1 , arr[ j ] = 2 两个元素不相等 i ++ , arr[ i ] = arr[ j ] , j++. 此时的数组为
arr = { 0 , 1 , 2 , 2 , 3} - 4 i = 2 , j = 4 ,arr[ i ] = 2 , arr[ j ] = 3 两个元素不相等 i ++ , arr[ i ] = arr[ j ] , j++. 此时的数组为
arr = { 0 , 1 , 2 , 3 , 3}
此时 j 已经扫描完整个数组,我们就可以退出循环了,然后i的位置就是我们不重复元素的最后一个下标
下面试代码
//消除数组中的重复元素
public class LeetCode26 {
public int removeDuplicates(int[] nums) {
if (nums.length == 0)
return 0;
int i = 0;
//用后面不重复的数字替换前面重复的数字 i记录重复数字的下标 j则扫描不重复的数字
//这样只能解决以排序的数组
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
public static void main(String[] args) {
int[] nums = { 1, 2 ,3, 4 , 5};
LeetCode26 test = new LeetCode26();
System.out.println(test.removeDuplicates(nums));
}
}
LeetCode80
现在我们在26的基础上提升一下难度,如果我们允许出现次数为2次,那应该怎么解决呢。
我们只要设置一个计数器就可以啦,计数器在每次遇到不重复时就清0,这样我们就可以确保每次的新数字都是从0开始算,如果计数器达到2时,我们后面如果在扫描到这个数,我们就把这个数删掉
下面的是代码
//消除数组中的重复元素
//重复元素允许出现两次 本题如同26题 只是重复的元素可以出现两次 所以下面设置一个flag标识元素出现的次数
public class LeetCode80 {
public int removeDuplicates(int[] nums) {
int flag = 0;
if (nums.length == 0)
return 0;
int i = 0;
// 两个指针i,j分别重起点开始 j先走如果没有重复 则替换i+1个 (本题容重复个数为2) 设置flag标识重复数字的个数 如果遇到不同的数字则重置
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
flag = 0;
} else {
if (++flag < 2) {
i++;
nums[i] = nums[j];
continue;
}
}
}
return i + 1;
}
}
LeetCode27
要求: 删除数组中给定的数,要求不能开辟新的空间,且数组不排序.
思路:我们定义两个指针 l , r 其中 l 指向数组开头 r 指向数组末尾,我们用 i 遍历数组,如果 l 的位置是我们要删除的元素,我们就把 l 和 r 的元素交换,那么我们是不是就把要删除的元素放到最后去了,但是你放到后面完之后就不能在继续做交换了,所以 r 就往前移动一位,但是这时候 l 的位置还不能忘后移动,因为此时交换后的数还需要做比较所以继续比较。依次类推 我们当 l 碰到 r 时就退出循环。
下面是代码
//使用双指针 把满足的数放到数组的末尾
public int removeElement(int[] nums, int val) {
int l = 0, r = nums.length -1;
while(l <= r) {
if(nums[l] == val) {
int temp = nums[l];
nums[l] = nums[r];
nums[r] = temp;
r--;
}else {
l++;
}
}
return l;
}
总结:我们根据上面的题目可以看到,数组类的题目很多是运用双指针技术加上给定的特殊条件进行解决,至于指针的位置(是两个都从头开始走还是一个在头一个在尾进行指针碰撞)的设定还是需要我们进行仔细的思考。如果后续还有这样的题目我会一一补充,感谢阅读,加油!