算法——双指针

- 日理万妓 2024-02-18 13:03 178阅读 0赞

一、背景知识

  • 双指针(Two Pointers):指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。
  • 对撞时针

    • 两个指针方向相反
    • 对撞指针一般用来解决有序数组或者字符串问题
  • 快慢指针:

    • 两个指针方向相同,速度不同。移动快的指针被称为 「快指针(fast)」,移动慢的指针被称为「慢指针(slow)」
    • 快慢指针一般用于处理数组中的移动、删除元素问题,或者链表中的判断是否有环、长度问题。
  • 分离双指针:

    • 两个指针分别属于不同的数组 / 链表
    • 分离双指针一般用于处理有序数组合并,求交集、并集问题。
  • 滑动窗口法:

    • 两个指针,一前一后组成滑动窗口,并计算滑动窗口中的元素的问题。一般是右端向右扩充,达到停止条件后右端不动,左端向右端逼近,逼近达到停止条件后,左端不动,右端继续扩充。
    • 滑动窗口法一般用于处理字符串匹配问题和子数组问题
  • 时间复杂度O(n)

二、例题

总结:

  • 要移动多个指针的情况可以分解成双指针的情况(1+n)
  • 关键是什么条件下移动哪个指针,分开思考,一起走就容易乱

1、移动零(快慢指针)

" class="reference-link">8afadb98a6ff4368b6064f84eb209a6c.png

突破点:右指针是贴着左指针开始往右移的,不是从数组右端往左移,因为那样会搅乱数组元素的相对顺序

  1. class Solution {
  2. public void moveZeroes(int[] nums) {
  3. int l=0;//左指针
  4. int r=0;//右指针
  5. int count=0;//计算0的个数
  6. while(r<nums.length){
  7. if(nums[r]!=0 && nums[l]==0){//找到0,交换后,左右指针都往右移
  8. nums[l]=nums[r];
  9. nums[r]=0;
  10. count++;
  11. l++;
  12. }
  13. if(nums[l]!=0){//没找到0,左右指针都往右移
  14. l++;
  15. }
  16. //前两种情况都有r++; 所以把它提到外面,大家都能用
  17. r++;//如果只有左指针找到0,右指针继续往右移
  18. }
  19. }
  20. }

d9e3b7183967402abb7bb1e8fab421e7.jpeg

7ad5f2dc92aa4864b054e314a05add0b.png

代码优化:封装方法

  1. class Solution {
  2. public void moveZeroes(int[] nums) {
  3. int n = nums.length, left = 0, right = 0;
  4. while (right < n) {
  5. if (nums[right] != 0) {
  6. swap(nums, left, right);
  7. left++;
  8. }
  9. right++;
  10. }
  11. }
  12. public void swap(int[] nums, int left, int right) {
  13. int temp = nums[left];
  14. nums[left] = nums[right];
  15. nums[right] = temp;
  16. }
  17. }

2、盛最多水的容器(对撞指针)

c8f3cdefdbaf4265adbf14ce7b69d206.png

01289209f3774836a78b1e29afd9e90e.png

突破点:不是同时移动两个指针,而是只移动较小的那个,因为较小的值改变会影响整体面积

  1. public class Solution {
  2. public int maxArea(int[] height) {
  3. int l = 0, r = height.length - 1;//左右指针分别指向数组的头尾
  4. int ans = 0;//维护一个最大值
  5. while (l < r) {
  6. int area = Math.min(height[l], height[r]) * (r - l);//当前面积,不一定是最大值
  7. ans = Math.max(ans, area);
  8. //移动数字较小的那个指针,整体面积才会发生变化
  9. if (height[l] <= height[r]) {
  10. ++l;
  11. }
  12. else {
  13. --r;
  14. }
  15. }
  16. return ans;
  17. }
  18. }

b8f55ad176854faf901d8755d2e1c9b9.jpeg

3、三数之和(对撞指针)

8fb99e6c13a8453684e57215bd03615b.png

c494a921d25549659d151614ea6449f0.png

突破点:三个指针的相对方位不会改变,一左一中一右,
它们也不会走对方走过的路,因为它们三个实际上是相同的
如果改变了方位,就会出现重复的三元组

  1. class Solution {
  2. public List<List<Integer>> threeSum(int[] nums) {
  3. int n = nums.length;
  4. Arrays.sort(nums);
  5. List<List<Integer>> ans = new ArrayList<List<Integer>>();
  6. // 枚举 a
  7. for (int first = 0; first < n; ++first) {
  8. // 需要和上一次枚举的数不相同
  9. if (first > 0 && nums[first] == nums[first - 1]) {
  10. continue;
  11. }
  12. // c 对应的指针初始指向数组的最右端
  13. int third = n - 1;
  14. int target = -nums[first];
  15. // 枚举 b
  16. for (int second = first + 1; second < n; ++second) {
  17. // 需要和上一次枚举的数不相同
  18. if (second > first + 1 && nums[second] == nums[second - 1]) {
  19. continue;
  20. }
  21. // 需要保证 b 的指针在 c 的指针的左侧
  22. // 因为当b走到c的右边时,相当于原来的c,会有重复的三元组产生
  23. while (second < third && nums[second] + nums[third] > target) {
  24. //b和c之和大于target,b是往右走值是增大的,所以要移动c指针往左走
  25. --third;
  26. }
  27. // 如果指针重合,随着 b 后续的增加
  28. // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
  29. if (second == third) {
  30. break;
  31. }
  32. //找到了
  33. if (nums[second] + nums[third] == target) {
  34. List<Integer> list = new ArrayList<Integer>();
  35. list.add(nums[first]);
  36. list.add(nums[second]);
  37. list.add(nums[third]);
  38. ans.add(list);
  39. }
  40. }
  41. }
  42. return ans;
  43. }
  44. }

fb592bd2cc0a4153b193576f002dc514.jpeg

4、接雨水(对撞指针)

cba1115291a0430f8cca1dafd36f2991.png

35b42b264bab48b1b7cad93cd29dfbba.png

突破点:是从两边向中间逼近,而非从左到右

  1. class Solution {
  2. public int trap(int[] height) {
  3. int ans=0;
  4. int left=0,right=height.length-1;//左右指针分别指向数组的头尾端
  5. int leftMax=0,rightMax=0;
  6. while(left<right){//指针未相撞
  7. leftMax=Math.max(leftMax,height[left]);
  8. rightMax=Math.max(rightMax,height[right]);
  9. if(height[left]<height[right]){
  10. ans+=leftMax-height[left];//当前height[left]夹在leftMax和rightMax中间,所以它的存水量受制于最小值
  11. ++left;//移动最小值,才会改变可存水量
  12. }else{
  13. ans+=rightMax-height[right];
  14. --right;
  15. }
  16. }
  17. }
  18. }

916a7d15b9f840c58fbbba2539423ea9.jpeg

发表评论

表情:
评论列表 (有 0 条评论,178人围观)

还没有评论,来说两句吧...

相关阅读

    相关 算法——指针

    一、背景知识 > 双指针(Two Pointers):指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。 >

    相关 算法指针题解

    双指针 算法解释:(直接照搬齐姐的了) 双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多 个数组的多个指针。 若两个指针指向同一数组

    相关 算法模板-指针

    简介 在很多数组问题中,双指针是一个反复被提及的解法。所谓双指针,指的是在对象遍历的过程中,并非单个指针进行访问,而是使用两个同向(快慢指针)或者反向(对撞指针)来进行扫