目录
双指针原理
这里的指针是用数组下标来充当指针,常用于数组分块或数组划分
双指针算法是指在遍历对象的过程中使用两个相同方向或者相反方向的指针单向移动进行扫描,从而解决问题。
有两种形式:
两个指针分别指向不同序列(归并排序的合并)
两个指针指向同一个序列(快排)
常见例题
移动零
https://leetcode.cn/problems/move-zeroes/description/
思路:
两个指针在这里的作用:
left :从左往右扫描,遍历数组
right: 已处理的区间内,非零元素的最后一个位置
1、两个指针将数组分为三个区间
在这道题中,[0,left]是数组相对顺序的非零元素,[left+1 , right -1]是非零元素,[right ,n-1]是还没有进行处理的数
2、
left = -1(因为一开始还没有处理好的值,所以将它设为-1;right = 0;
right遍历数组,遇到零元素right++;
非零元素:left++,交换left 和right的值,right++;
class Solution {
public void moveZeroes(int[] nums) {
//方法1
//直接遍历数组,然后将非0 的数覆盖0,最后的几个数就直接赋0
/* int s = 0;
for(int i = 0; i < nums.length;i++){
if(nums[i] != 0){
nums[s++] = nums[i];
}
}
while(s < nums.length){
nums[s++] = 0;
}
*/
//方法2
//双指针
//定义两个指针,一个遍历数组,一个分界非0 和0 的区间
for(int cur = 0, dest = -1; cur < nums.length;cur++){
if(nums[cur] != 0){
dest++;
int tmp = nums[cur];
nums[cur] = nums[dest];
nums[dest] = tmp;
}
}
}
}
复写零
思路:这道题中,两个指针需要从后往前移动(从前往后会覆盖掉值)
所以需要找到left最后指向的值,即最后一个复写的值
1、找到最后一个“复写”的值(双指针)
先判断left位置的值
如果是arr[left] != 0 ,right向后走一步,否则向后走两步
判断right是否已经到达边界
left++
2、处理边界,在第一步中,right可能会越界,所以判断right是否 > =n;
如果成立,将arr[n -1] = 0;left --; right -= 2;
3、“从后向前”完成复写操作
class Solution {
public void duplicateZeros(int[] arr) {
//使用双指针,从后往前走
int cur = 0,dest = -1,n = arr.length;
//1.先找到最后一个需要复写的数
while(cur < n){
if(arr[cur] != 0){
dest++;
} else{
dest += 2;
}
if(dest >= n-1) break;
cur++;
}
//2.处理边界情况,dest可能会出界
if(dest == n){
arr[n-1] = 0;
cur--;
dest -=2;
}
//3.从后向前完成复写,此时cur,和dest都已经在正确的位置上了
while(dest > 0){
if(arr[cur] != 0){
arr[dest] = arr[cur];
dest--;
} else{
arr[dest--] = 0;
arr[dest--] = 0;
}
cur--;
}
}
}
快乐数
鸽巢原理:有n个巢,n+1个鸽子,至少有一个巢穴里面的鸽子数大于1
步骤:
定义快慢指针
慢指针每次向后走一步,快指针每次向后走两步,
判断相遇的值是否为1
盛最多水的容器
利用单调性,使用双指针解决
有效三角形的个数
查找总价格数为目标值的两个商品(两数之和)
LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode)
相当于是两数之和,先排序
一个指针从左边开始,一个从右边开始,两个指针所指值相加,如果大于target,right--,如果小于target,left++;,等于就直接返回
三数之和
思路:同两数之和差不多,首先排序,先需要固定一个数,剩下的两个数就用双指针算法求,需要注意的是,题干中说不可以重复,所以需要去重和不能遗漏,所以需要加上去重条件,去重包括固定的数以及左右指针
满足条件的数加入中列表中,最后求得最终结果
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//先排序
Arrays.sort(nums);
List<List<Integer>> list = new ArrayList<>();
//2.先固定一个数a,a<=0
//3.在该数后面的的区间内,利用双指针算法快速找到两数之和等于-a的数
//注意去重,以及不要遗漏每个数
int n = nums.length;
for( int a = 0; a <n ;){
if(nums[a] > 0) break;
int b = -nums[a];
int left = a+1;
int right = nums.length-1;
while(left < right){
if(nums[left]+nums[right] < b) {
left++;
}else if(nums[left]+nums[right] > b ){
right--;
}else{
//收集数组
list.add(new ArrayList<Integer>(Arrays.asList(nums[a],nums[left],nums[right])));
left++;
right--;
//对left和right去重
while(left < right && nums[left] == nums[left-1])
left++;
while(left < right && nums[right] == nums[right+1])
right--;
}
}
//对a去重
a++;
while(a < nums.length && nums[a] ==nums[a-1])
a++;
}
return list;
}
}
四数之和
思路:
和三数之和一样的套路,相当于套娃
先固定一个数,然后再固定第二个数,最后双指针求最后两个数
四个数都需要去重
总结
通体来说,双指针的题大都体现在数组上面,大部分的题都是先想他的暴力解法,然后在暴力解法的基础上进行优化,最后用两个指针降低时间复杂度,多画图,多思考,每道题的情况都要考虑清楚。