hello-algo/docs/chapter_doublePointer/Double_pointer.md
2022-12-04 16:46:53 +08:00

5.1 KiB
Raw Blame History

算法技巧:双指针

双指针思想的理解

双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。双指针可以从不同的方向向中间逼近,即左右指针,也可以朝着同一个方向遍历,即快慢指针

通俗的说,就是在(数组,链表)遍历中,我们使用两个指针进行操作。所以双指针问题基本有以下几个细节:

  1. 双指针的初始位置。
  2. 双指针的移动方法。
  3. 遍历的结束条件。

快慢指针

快慢指针 指的是两个指针从同一侧开始遍历数组,将这两个指针分别定义为快指针fast慢指针slow两个指针以不同的策略移动直到两个指针的值相等或其他特殊条件为止如快指针fast每次增长两个慢指针slow每次增长一个。

PS

慢指针slow一般指向数组的第一个元素 快指针fast一般指向数组的第一个元素

用一个小案例理解快慢指针在数组中的作用

移除数组中的元素

有一个数组nums 我们需要根据输入的 target来移除数组中的元素,不需要考虑数组中超出新长度后面的元素

例如数组内容是:[3, 2, 2, 3] target = 3

那么函数返回的长度应该是 2 取出3之后只需要数组前两位是正确的也就是 [2,2] 就算成功 后续的不用考虑)

示例思路图

image-20221204164516333

根据示例图思路我们此时再讲述快慢双指针的使用方法 :

  1. 一般来说 fast指针与slow指针的按照区间前后移动 在移动的过程中去处理数据

  2. 其实快慢指针的移动区间不一定是非要 fast+2 slow+1 区间可以根据不同需要灵活变化

  3. 一般来说使用快慢双指针的复杂度是 O(N)

解题 Code

=== "Java"

```java title="double_pointer.java"
   public static void main(String[] args) {
        int[] arr = {3,2,2,3};
        int res = removeElement(arr, 3);
        System.out.println("有效长度"+res+" :"+" 数组内的数据"+ Arrays.toString(arr));
    }

       /*  快慢指针案例 */
    static int removeElement(int[] nums, int val) {
        int fast = 0, slow = 0;
        while (fast < nums.length) {
            /*
             * 如果nums[fast] = target 那么fast++
             * 如果nums[fast] != target nums[slow]与nums[fast]交换
             */
            if (nums[fast] != val) {
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
```

左右指针

这里引用一道入门的数组题目的双指针解法来理解对撞双指针

两数之和 II 有序数组

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列和一个整数目标值 target请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

示例

输入nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]

示例思路图

image-20221204164550069

根据示例图思路我们此时再讲述相向左右双指针的使用方法 :

  1. 根据条件判断去调整left和right指针的位置 来达到想要的效果

  2. 一般来说使用左右双指针的复杂度是 O(N)

解题 Code

=== "Java"

```java title="double_pointer.java"
   public static void main(String[] args) {
        int[] arr = {2,7,11,15};
        int[] result = twoSum(arr, 9);
        System.out.println("结果集:"+ Arrays.toString(result));
    }

    /*  相向左右指针案例  */
    static int[] twoSum(int[] nums, int target) {
        // 一左一右两个指针相向而行
        int left = 0, right = nums.length - 1;
        while (left < right) {
            int sum = nums[left] + nums[right];
            if (sum == target) {
	// 题目要求下表从1开始
                return new int[]{left + 1, right + 1};
            } else if (sum < target) {
                left++; // 让 sum 大一点
            } else if (sum > target) {
                right--; // 让 sum 小一点
            }
        }
        return new int[]{-1, -1};
    }
```

总结

快慢指针和左右指针都是使用双指针的技巧:

  1. 快慢指针一般应用于需要遍历比较或者是数组,链表的元素操作

  2. 左右指针一般应用于数组内的元素操作上

双指针的技巧就是为了简化我们在遍历的时候函数的复杂度,很多需要嵌套循环才能解决的问题 用双指针往往可以获得优化。