HOT100-双指针

发布于 2024-02-01  207 次阅读


AI 摘要

标题:HOT100-双指针 摘要:双指针是一种常用的解题方法,在算法题中有多个经典的问题可以使用双指针来解决。其中,移动零可以使用双指针的方法来将0移动到数组的末尾;盛最多水的容器通过左右指针的移动来确定每次计算的容量,最后取最大值;三数之和使用左右指针的方式来寻找三个数相加等于目标值的组合;接雨水可以通过动态规划和双指针的结合来计算。双指针的方法在解决这些问题时非常高效。

283. 移动零

class Solution {
    public void moveZeroes(int[] nums) {
        int left = 0,right = 0,len = nums.length;
        for(int i = 0;i < len;i++){
            if(nums[i] == 0){
                left = i;//若为0 放置左指针
                for(int j = left;j < len;j++){//开始移动右指针
                    if(nums[j] != 0){//找到不为0的右指针
                        right = j;
                        int temp = nums[right];
                        nums[right] = nums[left];
                        nums[left] = temp;//交换左右指针
                        break;//结束
                    }
                }
            }
        }
    }
}

11. 盛最多水的容器

class Solution {
    public int maxArea(int[] height) {
        int len = height.length;
        int left = 0,right = len - 1;//初始化两个指针 一个在最左 一个在最右
        int max = 0;//初始化max
        while(left < right){//确定右指针只能在左指针右边 保证不会越界
            if(height[left] > height[right]){//若左比右高 就按右边的算面积
                max = Math.max((right - left) * height[right],max);
                right--;
            }else{//若右比左高 就按左边的算面积
                max = Math.max((right - left) * height[left],max);
                left++;
            }
        }
        return max;
    }
}

15. 三数之和

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        /**
        本题主要是去重
         */
        int len = nums.length;
        Arrays.sort(nums);//首先排序 默认从大到小
        List<List<Integer>> list = new ArrayList<List<Integer>>();
        for(int i = 0;i < len - 2;i++){//i 是第一个指针,用来表示第一个数的位置
            if(nums[i] > 0) break;//去重:如果第一个数就大于0 剩下的数都会大于0 不可能得出结果
            if(i != 0 && nums[i] == nums[i-1]) continue;//如果第一个数和上一个数重复 去重
            int m = i + 1,n = len - 1;//确定两个指针的位置 即左指针为第一个数+1 右指针为最后一位
            while(m < n){//开始收缩
                int sum = nums[i] + nums[m] + nums[n];//用和来判断如何收缩
                if(sum == 0){//找到了结果为0
                    list.add(Arrays.asList(nums[i],nums[m],nums[n]));//加入list作为答案
                    while(m < n && nums[m] == nums[m + 1]) m++;//如果左指针有相同的情况 就继续移动去重
                    while(m < n && nums[n] == nums[n - 1]) n--;//如果右指针有相同的情况 就继续移动去重
                    m++;//收缩左指针
                    n--;//收缩右指针
                }else if(sum > 0){//如果和大于0 那么移动右指针 让右边值变小
                    n--;
                }else if(sum < 0){//如果和小于0 那么移动左指针 让左边值变大
                    m++;
                }
            }
        }
        return list;
    }
}

42. 接雨水

class Solution {
    public int trap(int[] height) {
        /**
        栈可以做 但是动态规划加双指针做非常简单
         */
        /**
        单动态规划版
        int len = height.length;
        int[] max_left = new int[len];
        int[] max_right = new int[len];
        int sum = 0;
        for(int i = 1;i < len - 1;i++){
            max_left[i] = Math.max(max_left[i - 1],height[i - 1]);//给每个区域的值算出左边最高的
        }
        for(int i = len - 2;i >= 0;i--){
            max_right[i] = Math.max(max_right[i + 1],height[i + 1]);//给每个区域的值算出右边最高的
        }
        for(int i = 1;i < len - 1;i++){
            int min = Math.min(max_left[i],max_right[i]);//比较每个区域左右边最高的值 取最小
            if(min > height[i]){//若这个最小值大于当前值 证明是有蓄水空间的
                sum += min - height[i];//加上sum
            }
        }
        return sum;
         */
        /**
        栈版
        class Solution {
            public int trap(int[] height) {
                int size = height.length;

                if(size <= 2) return 0;
                Stack<Integer> stack = new Stack<Integer>();
                stack.push(0);

                int sum = 0;
                for(int index = 1;index < size;index++){
                    int stackTop = stack.peek();
                    if(height[index] < height[stackTop]){
                        stack.push(index);
                    }else if(height[index] == height[stackTop]){
                        stack.pop();
                        stack.push(index);
                    }else{
                        int heightAtIdx = height[index];
                        while (!stack.isEmpty() && (heightAtIdx > height[stackTop])){
                            int mid = stack.pop();

                            if (!stack.isEmpty()){
                                int left = stack.peek();

                                int h = Math.min(height[left], height[index]) - height[mid];
                                int w = index - left - 1;
                                int hold = h * w;
                                if (hold > 0) sum += hold;
                                stackTop = stack.peek();
                            }
                        }
                        stack.push(index);
                    }
                }
                return sum;
            }
        }
         */
        //双指针加动态规划版
        int n = height.length;
        int res = 0;
        // 左右指针:分别指向左右两边界的列
        int left = 0, right = n - 1;
        // 左指针的左边最大高度、右指针的右边最大高度
        int leftMax = height[left], rightMax = height[right];
        // 最两边的列存不了水
        left++;
        right--;
        // 向中间靠拢
        while(left <= right){
            leftMax = Math.max(leftMax, height[left]);
            rightMax = Math.max(rightMax, height[right]);
            if(leftMax < rightMax){
                /**
                这个循环有一点难看懂
                但是注意一点:height[left] 和 height[right]表示的是当前处理的值
                这是不断收缩的双指针,说明是在俩个方向收缩处理的
                当leftMax < rightMax时 证明当前左边的值更小 要用左边值来计算
                而为什么leftMax - height[left]呢?
                因为leftMax是先前和height[left]比较过的 证明leftMax是一定大于等于height[left]的
                所以可以不用比较直接结果相加
                */
                res += leftMax - height[left];
                left++;
            }else{
                // 右边同理
                res += rightMax - height[right];
                right--;
            }
        }
        return res;
    }
}

双指针的题目也不算难,记住left < right的循环公式就好。之前看接雨水栈的写法太难了,有些神化了这道题,再看动态规划的发现实在太简单了。双指针可以再看Acwing巩固一下格式就行。