1.移除元素
题目
解析
1.解析
- 左指针用来更新数组,右指针查找元素;
- 若右指针元素不等于目标值,左指针不断更新数组;若等于目标值,跳过该值即可;
- 时间复杂度:O(n);空间复杂度:O(1);
代码
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
// 时间复杂度:O(n)
// 空间复杂度:O(1)
int l = 0;
for(int r = 0;r < nums.size();r ++){
if(nums[r] != val){
nums[l ++] = nums[r];
}
}
return k;
}
};
2.删除有序数组中的重复元素
题目
解析
1.解析
- 由于数组是非递减的,所以只用比较当前数字和前一个数字是否相等,如果相等就直接跳过,不相等就让左指针更新数组即可;
- 自己写的是用哈希表,可以解决非有序数组情况,代码如下;
- 时间复杂度:O(n);空间复杂度:O(1);
代码
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
// 时间复杂度:O(n)
// 空间复杂度:O(1)
int n = nums.size();
int l = 1;
for(int r = 1;r < n;r ++){
if(nums[r] != nums[r - 1]) nums[l ++] = nums[r];
}
return l;
}
};
- 哈希表解决非有序情况:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
// 时间复杂度:O(n)
// 空间复杂度;O(非重复元素个数)
int n = nums.size();
unordered_map<int,int> p;
int l = 0;
for(int r = 0;r < n;r ++){
int x = nums[r];
if(p[x] == 0) {
p[x] ++;
nums[l ++] = nums[r];
}else {
continue;
}
}
return l;
}
};
3.删除有序数组中的重复元素II
题目
解析
1.解析
- 如果数组只有1 / 2个元素,直接返回;
- 这题和上题的区别就在于每个元素最多存两个,思路稍微改一下就可以,把当前数与左指针左边第二个数比较,不相等就更新数组,相等就跳过;
- 时间复杂度:O(n);空间复杂度:O(1);
代码
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
// 时间复杂度:O(n)
// 空间复杂度:O(1)
int n = nums.size();
if(n == 1 || n == 2) return n;
int l = 2;
for(int r = 2;r < n;r ++){
if(nums[r] != nums[l - 2]) nums[l ++] = nums[r];
}
return l;
}
};
4.移动0
题目
解析
1.解析
- 核心思想:左指针从头更新数组,右指针的数若不等于0,就将其与左指针的数交换;
- 时间复杂度:O(n);空间复杂度:O(1);
代码
class Solution {
public:
void moveZeroes(vector<int>& nums) {
// 时间复杂度:O(n)
// 空间复杂度:O(1)
int n = nums.size();
int l = 0;
for(int r = 0;r < n;r ++){
if(nums[r] != 0) swap(nums[l ++],nums[r]);
}
}
};
5.按奇偶排序数组
题目
解析
1.解析
- 核心思想:左指针从头更新数组,右指针的数为偶数,就将其与左指针的数交换;
- 时间复杂度:O(n);空间复杂度:O(1);
代码
class Solution {
public:
vector<int> sortArrayByParity(vector<int>& nums) {
// 时间复杂度:O(n)
// 空间复杂度:O(1)
int n = nums.size();
int l = 0;
for(int r = 0;r < n;r ++){
if(nums[r] % 2 == 0) swap(nums[l ++],nums[r]);
}
return nums;
}
};
6.按奇偶排序数组II
题目
解析
1.解析
- 核心思想:一组一组 偶数+奇数 的去更新数组;
- 找到偶数下标最左边的奇数,奇数下标最左边的偶数,进行交换;
- 时间复杂度:O(n);空间复杂度:O(1);
代码
class Solution {
public:
vector<int> sortArrayByParityII(vector<int>& nums) {
// 时间复杂度:O(n)
// 空间复杂度:O(1)
int n = nums.size();
int l = 0,r = 1;
// 核心:一组一组 偶数+奇数 地向后更新数组
while(l < n){
if(nums[l] % 2 == 0){ // 找到偶数下标最左边的奇数
l += 2;
}else if(nums[r] % 2 == 1){ // 找到奇数下标最左边的偶数
r += 2;
}else{
swap(nums[l],nums[r]);// 交换
l += 2;
r += 2;
}
}
return nums;
}
};
7.复写0
题目
解析
1.解析
- L: 从左边遍历元素,碰到 0 时则需要将 R-- 表示舍弃一个元素为复制 0 腾出空间。
- R:从数组尾部开始,指向下一个即将被抛弃的元素。
- K:指向下一个被复制元素需要去的位置。
- 难点:当 l == r 且当前元素为 0,既要保留(复写为2个0),又要舍弃1个0。所以最后还剩下一个0,直接放到填充位置;
- 时间复杂度:O(n);空间复杂度:O(1);
代码
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
// 时间复杂度:O(n)
// 空间复杂度:O(1)
int n = arr.size();
// l指向要保留的元素,r指向要删除的元素,k指向后方要填充的位置
int l = 0,r = n-1,k = n-1;
// 左指针找到一个0,右指针就要左移一位,即舍弃一个元素
for(l = 0;l < r;l ++){
if(arr[l] == 0) {
r --;
}
}
// 当 l == r 且当前元素为 0,既要保留(复写为2个0),又要舍弃1个0
// 所以最后还剩下一个0,直接放到填充位置
if(l == r && arr[l] == 0) arr[k --] = arr[r --];
while(r >= 0){
if(arr[r] == 0) {
arr[k --] = 0;// 复写0
}
arr[k --] = arr[r --];
}
}
};
8.总结
- 原地修改双指针步骤:
- 首先确定左右指针位置和移动方向;
- 其次,确定更新方式,一般有3种:
- 1.左指针更新数组,右指针移动寻找满足条件的元素 ,将右指针指向的数直接赋值给左指针。(适用于可以舍弃不满足条件的元素);
- 2.将左指针指向的数与右指针指向的数交换。(适用于将满足条件的数提前的题目);
- 3.左右指针同频移动,共同寻找不符合条件的数组对,进行交换;
- 复写0 这题比较有思想深度,用到了三指针,得多想多模拟;