Hot100之双指针

283移动零

题目

思路解析

那我们就把不为0的数字都放在数组前面,然后数组后面的数字都为0就行了

代码

class Solution {
    public void moveZeroes(int[] nums) {
        int left = 0;

        for (int num : nums) {
            if (num != 0) {
                nums[left++] = num;
                // left最后会变成数组中不为0的数的个数
            }
        }

        for (int i = left; i < nums.length; i++) {
            nums[i] = 0;
        }
        return;
    }
}

11盛最多水的容器

题目

思路解析

如果中间的水高度小,宽度又小,那肯定不会比蓝色这个区域大

如果中间的高度>=它的高度,但我们的宽度小

但我们容纳的水宽度变小高度不变

也不会比蓝色的面积大

因为中间的任何线都无法和他构成更大的容器了

因为我们 计算的高度是取决于短板的 ,宽度是变化的(收缩宽度甚至变小)

所以我们有个left左指针,right右指针

然后我们不断收缩,哪边高度小,哪边就开始收缩,因为计算的高度是取决于短板的

代码

class Solution {
    public int maxArea(int[] height) {
        int max = 0;
        int left = 0, right = height.length - 1;

        while (left < right) {
            // right - left 是我们的长度
            // 如果右边的高度大一点我们就 left++, 如果左边的高度大一点我们就 right-- 这样子不断收缩
            max = height[left] < height[right] ?
                    Math.max(max, (right - left) * height[left++]) :
                    Math.max(max, (right - left) * height[right--]);
        }

        return max;
    }
}

15三数之和

题目

三个数的和+起来为0

同时i!=j!=k

思路解析

我们要求的结果是nums【a】+nums【b】+nums【c】=0

我们的思路是for循环遍历a,然后求b,c

用两个指针操作,一个向右找一个向左找

我们先对数组进行排序,Arrays,sort()

如果我们的nums【0】>0,说明没负数,那说明我们的和不可能为0了我们直接return

然后判断去重,例如nus【i】==nums【i-1】我们就跳过,因为我们的nums【i-1】已经计算过答案了,我们【1,1,-2,0】我们没必要再利用第二个1了,所以我们直接去重

然后我们要判断和

因为我们的数组排序后是从小到大的

所以如果我们sum<0,我们就left++

如果我们的sum>0,我们就right--

如果我们找到了答案,我们就双指针收缩,left++,right--(双指针搜索前,我们要看看我们的判断条件,例如我们是找到最右边的left和最左边的right,这样子我们双指针收缩的时候,我们就不会得到重复的答案)

代码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();

        // 先对数组进行排序
        Arrays.sort(nums);

        // 如果排序后最小的元素都大于 0,不可能存在三个数之和为 0 的情况
        if (nums.length > 0 && nums[0] > 0) {
            return result;
        }

        // 遍历数组,固定一个数作为三元组的第一个数
        for (int i = 0; i < nums.length; i++) {
            // 去重:如果当前数和前一个数相同,跳过
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }

            int left = i + 1;
            int right = nums.length - 1;

            // 使用双指针法寻找另外两个数
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];

                if (sum > 0) {
                    // 和大于 0,右指针左移
                    right--;
                } else if (sum < 0) {
                    // 和小于 0,左指针右移
                    left++;
                } else {
                    // 找到一个三元组
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));

                    // 对第二个数和第三个数去重
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }

                    // 双指针同时收缩
                    left++;
                    right--;
                }
            }
        }

        return result;
    }
}

42接雨水

题目

我们要求下雨的时候我们能接多少水

思路解析

我们有三种解法,前后缀分解,双指针解法,单调栈写法

前后缀分解

我们要计算能接多少水,就要计算左边木板的高度和右边木板的高度,这两个高度取最小值

左边木板的高度取决于左边的最大高度

因为高于这个高度的水,他是会从左边流出去的

而低于这个高度的水,他是不会流出去的

右边的木板高度同理,它取决于右边的最大高度

我们要有一个数组要从左到右,存储前缀的最大值

还要有一个数组从右到左,存储后缀的最大值

我们可以Max(上一个前缀最大值,当前高度),这样子来取我们的前缀最大值

从左到右的前缀最大值

后缀最大值同理

然后我们把水桶里面能接的水都加起来,这样子我们就得到了答案


双指针(可以进行空间优化)

优化的点:我们不利用两个数组去收集我们的前后缀和,而是通过双指针不断移动来模拟max前缀和max后缀

因为我们的这个位置能收集的水取决于左边的最大高度和右边的最大高度之间的最小高度

所以我们单个位置单个位置收集

答案为:(左边的最大高度和右边的最大高度之间的最小高度)-当前高度

但一开始例如最左边节点,我们的左边的最大高度

最右边节点,我们的右边的最大高度是固定的

所以我们左边右边一个一个收集节点,然后通过左右指针更新我们的最大左右高度


单调栈

我们相当于把坑填平了,所以我们只要记录5和4就好了

例如2这个节点

我们5和4下标之间距离是宽度

Min(5,4)之间的最小值和当前节点的高度的差 就是高度

所以我们除了知道栈顶那个数是什么,还要知道栈顶那个数是什么

找上一个更大元素,在找的过程中填坑

如果收集的时候出现了5,3,6这种中间有凹的能收集的情况,我们就算这个位置

所以我们弄一个栈,每次出现凹凸情况的时候我们就收集雨水


代码

前后缀分解
class Solution {
    public int trap(int[] height) {
        int n = height.length;
        int[] preMax = new int[n]; // preMax[i] 表示从 height[0] 到 height[i] 的最大值
        
        preMax[0] = height[0];
        
        for (int i = 1; i < n; i++) {
            preMax[i] = Math.max(preMax[i - 1], height[i]);
        }

        int[] sufMax = new int[n]; // sufMax[i] 表示从 height[i] 到 height[n-1] 的最大值
        
        sufMax[n - 1] = height[n - 1];
        
        for (int i = n - 2; i >= 0; i--) {
            sufMax[i] = Math.max(sufMax[i + 1], height[i]);
        }

        int ans = 0;
        for (int i = 0; i < n; i++) {
            ans += Math.min(preMax[i], sufMax[i]) - height[i]; // 累加每个水桶能接多少水
        }
        
        return ans;
    }
}

==

双指针
class Solution {
    public int trap(int[] height) {
        int ans = 0;
        int left = 0;
        int right = height.length - 1;
        int preMax = 0; // 前缀最大值,随着左指针 left 的移动而更新
        int sufMax = 0; // 后缀最大值,随着右指针 right 的移动而更新
        while (left < right) {
        
            preMax = Math.max(preMax, height[left]);
            sufMax = Math.max(sufMax, height[right]);
            
            ans += preMax < sufMax ? preMax - height[left++] : sufMax - height[right--];
        }
        return ans;
    }
}

单调栈
class Solution {
    public int trap(int[] height) {
        int result = 0;
        int n = height.length;
        Deque<Integer> deque = new ArrayDeque<>();

        for (int i = 0; i < n; i++) {
            // 这个的意思是,如果收集的时候出现了5,3,6这种中间有凹的能收集的情况,我们就算这个位置
            while (!deque.isEmpty() && height[i] >= height[deque.peek()]) {
                int temp = height[deque.pop()];
                // 如果队列为空了,我们就跳出循环 
                if (deque.isEmpty()) {
                    break;
                }
                int left = deque.peek();
                int dh = Math.min(height[left], height[i]) - temp; // 面积的高
                result += dh * (i - left - 1);
            }
            deque.push(i);
        }
        return result;
    }
}
相关推荐
某个默默无闻奋斗的人1 分钟前
深度优先搜索(DFS)
算法·深度优先
圆圆滚滚小企鹅。7 分钟前
刷题记录 HOT100回溯算法-6:79. 单词搜索
笔记·python·算法·leetcode
带刺的坐椅38 分钟前
无耳科技 Solon v3.0.7 发布(2025农历新年版)
java·spring·mvc·solon·aop
逊嘘1 小时前
【Java数据结构】了解排序相关算法
数据结构·算法·排序算法
旧故新长2 小时前
Dijkstra算法解析
算法·图论
_周游2 小时前
【数据结构】_链表经典算法OJ(力扣/牛客第二弹)
数据结构·算法·链表
Kerwin要坚持日更3 小时前
一文讲解Java中的ArrayList和LinkedList
java·开发语言
sjsjs113 小时前
【数据结构-前缀树】力扣208. 实现 Trie (前缀树)
数据结构·leetcode
励志成为美貌才华为一体的女子3 小时前
python算法和数据结构刷题[3]:哈希表、滑动窗口、双指针、回溯算法、贪心算法
数据结构·算法·散列表