题目与题解
27. 移除元素
题目链接:27. 移除元素
代码随想录题解:27. 移除元素
解题思路
最简单的暴力法就是用一个新的数组作为结果数组,按顺序遍历原数组,如果遇到与val相同的值就跳过,否则塞入结果数组,最后返回结果数组即可。
但一刷已经知道解法了,这里采用覆盖而非新增的方式进行计算,既不影响其他数字的相对顺序,又可以以O(n)的时间复杂度和O(1)的空间复杂度得到结果。
设置两个指针,慢指针用来覆盖和替换数字,快指针用于判断当前数字是否为val。如果当前数字为val,快指针继续往前走;否则用快指针当前指向的数字替换慢指针的数字,然后慢指针前进,快指针再前进。
class Solution {
public int removeElement(int[] nums, int val) {
int i = 0, j = 0;
while (j < nums.length) {
if (nums[j] != val) {
nums[i++] = nums[j];
}
j++;
}
return i;
}
}
注意点
一开始写的时候可以多分两步写,熟了就可以合起来了,用for循环看起来会更优雅一点。
26.删除排序数组中的重复项
题目链接:26.删除排序数组中的重复项
解题思路
这题跟前一题区别不大,无非是快指针比较的对象由固定的val值变成了它的前一个有效数字,即慢指针指向的数字。
class Solution {
public int removeDuplicates(int[] nums) {
int slow = 0;
for (int fast = 1; fast < nums.length; fast++) {
if (nums[slow] != nums[fast]) {
slow++;
nums[slow] = nums[fast];
}
}
return slow+1;
}
}
注意点
最后返回的是长度,所以slow要加一。做的时候也要用例子模拟一下,避免指针覆盖的时候出错。
283.移动零
题目链接:283.移动零
解题思路
这题跟移动元素也是类似的,只是用于覆盖的val变成了0而已,以及最后需要返回调整后的数组,所以覆盖结束后剩下的数字要用0补齐。
class Solution {
public void moveZeroes(int[] nums) {
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != 0) {
nums[slow++] = nums[fast];
}
}
for (;slow < nums.length; slow++) {
nums[slow] = 0;
}
}
}
注意点
无
844.比较含退格的字符串
题目链接:844.比较含退格的字符串
leetcode题解:844.比较含退格的字符串
解题思路
同样还是用类似的思路去覆盖不需要的值,这题特殊点在于存在退格键,所以如果碰到退格键,一次需要跳过两个字符,所以碰到退格键时,慢指针还需要后退一步。
class Solution {
public boolean backspaceCompare(String s, String t) {
return backspaceDelete(s).equals(backspaceDelete(t));
}
String backspaceDelete(String s) {
char[] chars = s.toCharArray();
int slow = 0;
for (int fast = 0; fast < s.length(); fast++) {
if (chars[fast] == '#') {
slow = Math.max(0, slow-1);
} else {
chars[slow++] = chars[fast];
}
}
return new String(chars).substring(0, slow);
}
}
注意点
1. String和chars的转换:
char[] chars = s.toCharArray();
new String(chars).substring(0, slow);
2. Java的String是不能修改的,所以不像c++可以直接当数组用那么方便,这样做空间效率只能是O(n),跟用栈来做相比,减少了出入栈的过程,时间省下一些。
3. 官方题解的双指针法实在是有点费解,跳过吧。
977.有序数组的平方
题目链接:977.有序数组的平方
代码随想录题解:977.有序数组的平方
解题思路
一般来说,数组题要求时间复杂度是O(n)的,多用双指针完成。
设置头尾两个指针left和right,以及结果数组res。由于最大结果一定在数组两头(因为绝对值大),所以计算时先从大的开始遍历,即left向右走,right向左走。每次循环的时候,比较left和right指针对应的数字大小,将更大的那个放入结果数组中,指针再前进,直到所有数字遍历结束。
class Solution {
public int[] sortedSquares(int[] nums) {
int[] res = new int[nums.length];
int left = 0, right = nums.length-1;
for (int i = nums.length - 1; i >= 0; i--) {
int sq1 = nums[left]*nums[left], sq2 = nums[right]*nums[right];
if (sq1 > sq2) {
res[i] = sq1;
left++;
} else {
res[i] = sq2;
right--;
}
}
return res;
}
}
注意点
写的时候脑子要清醒一点,什么时候用下标什么时候取数字千万不要再写错了。
以及一开始想的时候想复杂了,想找到数组中间一个是负数一个是正数的地方再开始用双指针依次遍历计算,多了一次查找,还很有可能出错,直接从两头开始就很好。
今日收获
复习了一下移除元素系列的题目,和有序数组,巩固了双指针的用法。
1. 对于数组问题,如果时间复杂度要求较高,双指针是非常常用的方法,但是具体怎么指,是快慢指针,还是两头指针,或是后面的滑动窗口这样的指针,需要根据题目分析。
2. 对于需要删除元素但又要保留原数组顺序的数组代码题,移除元素中所用的快慢指针+元素覆盖方法非常实用,效率很高,也不影响相对顺序。