算法——双指针

一、背景知识 

  • 双指针(Two Pointers):指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。
  • 对撞时针
    • 两个指针方向相反
    • 对撞指针一般用来解决有序数组或者字符串问题
  • 快慢指针:
    • 两个指针方向相同,速度不同。移动快的指针被称为 「快指针(fast)」,移动慢的指针被称为「慢指针(slow)」
    • 快慢指针一般用于处理数组中的移动、删除元素问题,或者链表中的判断是否有环、长度问题。
  • 分离双指针:
    • 两个指针分别属于不同的数组 / 链表
    • 分离双指针一般用于处理有序数组合并,求交集、并集问题。
  • 滑动窗口法:

    • 两个指针,一前一后组成滑动窗口,并计算滑动窗口中的元素的问题。一般是右端向右扩充,达到停止条件后右端不动,左端向右端逼近,逼近达到停止条件后,左端不动,右端继续扩充。

    • 滑动窗口法一般用于处理字符串匹配问题和子数组问题
  • 时间复杂度O(n)

二、例题

总结:

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

 1、移动零(快慢指针)

算法——双指针_第1张图片

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

class Solution {
    public void moveZeroes(int[] nums) {
        int l=0;//左指针
        int r=0;//右指针
        int count=0;//计算0的个数
        while(r

算法——双指针_第2张图片

算法——双指针_第3张图片

 代码优化:封装方法

class Solution {
    public void moveZeroes(int[] nums) {
        int n = nums.length, left = 0, right = 0;
        while (right < n) {
            if (nums[right] != 0) {
                swap(nums, left, right);
                left++;
            }
            right++;
        }
    }

    public void swap(int[] nums, int left, int right) {
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

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

算法——双指针_第4张图片

算法——双指针_第5张图片

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

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

算法——双指针_第6张图片

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

算法——双指针_第7张图片

算法——双指针_第8张图片

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

class Solution {
    public List> threeSum(int[] nums) {
        int n = nums.length;
        Arrays.sort(nums);
        List> ans = new ArrayList>();
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                // 因为当b走到c的右边时,相当于原来的c,会有重复的三元组产生
                while (second < third && nums[second] + nums[third] > target) {
                    //b和c之和大于target,b是往右走值是增大的,所以要移动c指针往左走
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b list = new ArrayList();
                    list.add(nums[first]);
                    list.add(nums[second]);
                    list.add(nums[third]);
                    ans.add(list);
                }
            }
        }
        return ans;
    }
}

算法——双指针_第9张图片

4、接雨水(对撞指针)

算法——双指针_第10张图片

算法——双指针_第11张图片

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

class Solution {
    public int trap(int[] height) {
        int ans=0;
        int left=0,right=height.length-1;//左右指针分别指向数组的头尾端
        int leftMax=0,rightMax=0;
        while(left

算法——双指针_第12张图片

你可能感兴趣的:(数据结构与算法(java版),算法,java)