什么是双指针
在leetcode中 有一类题 可以采用双指针的方法:
不同于普通的遍历,使用单指针(单循环)的方法进行遍历,而是采用双指针(双循环)的方法进行相应的遍历,从而达到相应的目的
在使用的时候,我们通常设定左右两边指针向中间移动,亦或者在一边两个指针进行相应的移动。需要结合实际情况和场景进行相应 的选择,以及变通
虽然在上面一直在强调指针,但其实在Java中并没有指针的概念,随之而来的是索引,这一点还是说清楚。
说了那么多直接来看看例子叭 |
双指针Leetcode例题
1.盛水最多的容器
思路:
这道题是一道双指针类型的题目,分析题意决定盛水多少的量其实一直是短板决定的,我们需要找到的是一个较长的板和一个较短的板,同时两者的距离相对较长这也就可以构造出一个盛水最多的容器。
当开始做这道题的时候,我们分别在左右0以及nums.length-1
处各放置一个指针分别为L以及R;然后我们记录下当前的面积;然后我们把此时的L与R对比长度,永远移动的是小的那块板,并且将新得到的面积与以前记录的面积进行相应的比较,永远取大的那一块,这样子最终当L==R
的时候,此时面积为最大。
代码实现:
class Solution {public int maxArea(int[] height) {
//最小的短板决定了 你能装多少水
int left = 0, right = height.length-1, maxSquare = 0;
//开始循环
while(left<right){
maxSquare =height[right]<height[left] ?
Math.max(maxSquare,(right-left)*height[right--]):
Math.max(maxSquare,(right-left)*height[left++]);
}
return maxSquare;
}
}
2.三数之和
思路:
这道题同样也是一道典型的双指针题目
当开始做这道题的时候,我们需要先对杂乱无章的数组nums[]
进行排序Arrays.sort();
排序后的数组利用一个for循环,以及分别在i+1和nums.length
位置的两个左右指针进行相应的移动,当sum>0的时候我们需要把最大值调小就是把num.length
的指针--
,当sum<0的时候需要把i+1位置的指针进行++
随时记录满足条件的三元组,但在i+1以及nums.length
的地方我们需要注意去重的操作
while (L<R&&nums[L]==nums[L+1]) L++;//去重
while (L<R&&nums[R]==nums[R-1]) R--;//去重
同时当i>0的时候我们需要对i+1和i相比较,如果等于的话continue这次循环就没必要了
代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//先进行数组排一下序
Arrays.sort(nums);
//定义返回结果集
List<List<Integer>> resultList = new ArrayList<>();
//如果nums 小于0的话要直接返回的哦 否则会报奇怪的异常的
if (nums.length<3) return resultList;
for (int i=0;i<nums.length;i++){
//如果第一个数都已经大于0了 我们已经经过了排序 还这样 那证明最后的结果 不会出现0的和了
if (nums[i]>0) return resultList;
//还需要 在外面判定一下是否重复
if (i>0&&nums[i]==nums[i-1]) continue;//判重
//定义一下左指针和右指针
int L = i+1;
int R = nums.length-1;
//进行左右指针的移动
while (L<R){
int sums = nums[i]+nums[L]+nums[R];
if (sums == 0){
resultList.add(Arrays.asList(nums[i],nums[L],nums[R]));
while (L<R&&nums[L]==nums[L+1]) L++;//去重
while (L<R&&nums[R]==nums[R-1]) R--;//去重
L++;//这个jiajia跟上面得不一样!!!!
R--;
}
//根据sums得值调整双指针得位置
else if (sums>0) R--;
else if (sums<0) L++;
}
}
return resultList;
}
}
3.最接近的三数之和
思路:
最接近的三数之和,首先需要的也是先对于杂乱的
nums[]
进行相应的排序,利用Arrays.sort();
相应的排序,得到一个递增的数组,与上面一道题的思路几乎一致。设置三个指针,第一个为i
位置,第二个为i+1
位置,第三个在nums[].length-1
的位置。然后我们取一个sum=nums[0]+nums[1]+nums[2];
随后我们移动左右指针,去寻找最接近的差值通过求取绝对值进行比较,利用Math.abs()
。最后我们返回取到的三个值即可
代码:
class Solution {
public int threeSumClosest(int[] nums, int target) {
//先对数组排序
Arrays.sort(nums);
//先得到一个基准得和
int sum = nums[0]+nums[1]+nums[2];
for (int i =0;i<nums.length;i++){
//定义左右指针
int L = i+1;
int R = nums.length-1;
//进行双指针的移动
while (L<R){
int dValueSum = nums[i]+nums[L]+nums[R];
//这个if为储值得过程
if (Math.abs(dValueSum-target)<Math.abs(sum-target)) sum = dValueSum;//谁离target近就换成谁
//下面都是移动指针的操作
if (dValueSum>target) R--;
else if (dValueSum<target) L++;
else return sum;
}
}
return sum;
}
}
4.四数之和
思路
这次需要设置四个指针,但经常移动的还是双指针。我们需要分别在
i
i+1
i+2
nums.length-1
设置四个指针
1.如果本nums[].length
的长度小于四,就没必要开始这次的循环
2.首先如果在k>0
的情况下,即已经完成第一次循环的基础上,如果此时的nums[k-1]==nums[k]
就证明需要continue,因为重复 了。还有一种情况,我们的sum此时最大能取到的是nums[k]+nums[length-1]+nums[length-2]+nums[length-3]
,如果说这个值都小 于 target,那证明这次的循环也没必要进行,直接进行下次就行。此时我们同样可以得到整个数组中可以取到的最小的和,其中我 们可以得知sum的值为nums[k]+nums[k+1]+nums[k+2]+nums[k+3]
如果着这个最小值都小于target那没必要循环了 直接break
3.第二个指针位于k+1的位置,首先也是当k+1>0
我们进行相同于第二步的去重操作,同时我们建立所对应的左右指针,分别处于k+2
以及nums[].length-1
的位置,分别命名为L和R指针此时的最小值为nums[k]+nums[k+1]+nums[L]+nums[L+1]
,如果小于我们 break,也能取得本次循环的最大值nums[k]+nums[k+1] + nums[R] + nums[R-1];
如果target大于sum continue。
4.移动左右指针,将符合的记录下来,同时每次移动指针进行与target比较前,先去重
代码
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
//建立一个结果的集合类
List<List<Integer>> resultList = new ArrayList<>();
//建立一个数组长度int
int len = nums.length ;
//如果数组的长度小于4的话 就返回 这个结果集
if ( nums == null||len < 4 ) {
return resultList;
}
//对数组进行排序
Arrays.sort(nums);
//进行第一层循环 建立指针k
for (int k = 0; k < len - 3; k++) {
//去重操作
if ( k > 0 && nums[k] == nums[k - 1] ) continue;
//得到最大值 如果说target大于这个最大值就没必要这次的循环
int maxK = nums[k] + nums[len-1] + nums[len - 2] + nums[len - 3];
if ( target > maxK ) continue;
//得到这个数组的最小的和 如果说target小于这个最小值 整个循环都可以跳出了
int minK = nums[k] + nums[k + 1] + nums[k + 2] + nums[k + 3];
if ( target < minK ) break;
//进行第二次循环操作建立指针i
for (int i = k + 1; i < len - 2; i++) {
//去重操作
if ( i > k + 1 && nums[i] == nums[i - 1] ) continue;
//建立左右指针
int L = i + 1;
int R = len-1;
//得到当前的最大值 如果说target大于这个最大值就没必要这次的循环
int maxI = nums[k] + nums[i] + nums[R] + nums[R-1];
if ( target > maxI ) continue;
//得到最小值
int minI = nums[k] + nums[i] + nums[L] + nums[L + 1];
if ( target < minI ) break;
while (L < R) {
//移动指针的值记录下来
int ans = nums[k] + nums[i] + nums[L] + nums[R];
//如果相等的话 就记录下来
if ( ans == target ) {
resultList.add(Arrays.asList(nums[k], nums[i], nums[L], nums[R]));
L++;
while (L < R && nums[L] == nums[L - 1]) L++;
R--;
while (L < R && nums[R] == nums[R + 1]) R--;
} else if ( ans > target ) {
R--;
} else {
L++;
}
}
}
}
return resultList;
}
总结
这四道题,尤其后面三道有递进的感觉,都直接或者间接采用了双指针的方法,记录一下自己的思路希望能给自己以及他人带来帮助