目 录CONTENT

文章目录

双指针&滑窗

FatFish1
2025-08-24 / 0 评论 / 0 点赞 / 19 阅读 / 0 字 / 正在检测是否收录...

双指针的题目中一般有明显的两个条件:最大最小、最左最右...

找到移动两个指针的条件:

  • 例如算最大差值,则next > current,移动max指针并计算差值,next < current,移动min + max指针

  • 例如算最大面积,先在左右端点,往中间移动,每次移动小边

【leetcode.121 买卖股票的最佳时机】

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
class Solution {
    public int maxProfit(int[] prices) {
        int min = 0;
        int max = 0;
        int result = 0;
        for (int i = 0; i < prices.length; i ++) {
            if (prices[i] > prices[min]) {
                max = i;
                int temp = prices[max] - prices[min];
                result = Math.max(temp, result); 
            } else {
                min = i;
                max = min;
            }
        }
        return result;
    }
}

典型的双指针,因为题目里找最大利润,即一个最大,一个最小

【leetcode11. 盛水最多的容器】

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

class Solution {
    public int maxArea(int[] height) {
        // 双指针:代表两个边界,每次移动小边
        // 为什么是小边?因为假设t是宽,min(x,y)是高,如果min(x,y)结果是x,如果移动y,新的y比x大,那就是宽缩小了,高不变,如果新的y比x小,那就是宽缩小,高也缩小
        int x = 0;
        int y = height.length - 1;
        int area = 0;
        while (true) {
            if (y <= x) {
                break;
            }
            int temp = (y - x) * Math.min(height[x], height[y]);
            area = Math.max(area, temp);
            if (height[x] <= height[y]) {
                x += 1;
            } else {
                y -= 1;
            }
        }
        return area;
    }
}

【leetcode.202 快乐数 - 快慢指针】

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。

  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。

  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

此题中提到一个点:要么无限循环,要么到达终点——1。应对无限循环的问题,一定是双指针中的快慢指针,快慢指针有两个要点:

  • 要动,一个动得快,一个动的慢,体现在java上就是一个getNext(n),一个是getNext(getNext(n))

  • 不动的很可能进入不了循环

  • 如果会无限循环,最终只有一个结果:在某一个点上两个指针相遇,如果不能循环,则快指针一定会到达终点

class Solution {
    public boolean isHappy(int n) {
        if (n == 1) {
            return true;
        }
        int slow = n;
        int fast = getNext(n);
        while (true) {
            if (slow == fast) {
                return false;
            } else if (fast == 1) {
                return true;
            }
            slow = getNext(slow);
            fast = getNext(getNext(fast));
        }
    }

    public int getNext(int n) {
        char[] charArray = String.valueOf(n).toCharArray();
        int sum = 0;
        for (char c : charArray) {
            sum += Integer.parseInt(String.valueOf(c)) * Integer.parseInt(String.valueOf(c));
        }
        return sum;
    }
}

【leetcode. 26、27 移除元素】

26题,移除重复元素,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
class Solution {
    public int removeDuplicates(int[] nums) {
        int index = 0;
        for (int num : nums) {
            if (index == 0) {
                index += 1;
                continue;
            }
            if (num != nums[index - 1]) {
                nums[index] = num;
                index += 1;
            }
            
        }
        return index;
    }
}

27题,删除特定元素:

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3,_,_,_]
解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。
注意这五个元素可以任意顺序返回。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
class Solution {
    public int removeElement(int[] nums, int val) {
        int index = 0;
        for (int num : nums) {
            if (num != val) {
                nums[index] = num;
                index += 1;
            }
        }
        return index;
    }
}

这两个题使用双指针,因为有一个很明显的题意:移除并往前插

这就意味着有一个指针正常遍历,有一个指针落后,即双指针问题

如果想不到使用双指针,暴力破解也是可以的,只是会麻烦一点,效率也不高,因此它非常典型

【leetcode.76 最小覆盖字串 - 双边指针】

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

这道题的双指针解法就是:

  • 如果覆盖,左边移动,如果不覆盖,右边移动

  • 可以设置一个标志位,判定在while循环中是哪边进行移动

class Solution {
    public String minWindow(String s, String t) {
        if (t.length() > s.length()) {
            return "";
        }
        int left = 0;
        int right = 0;
        Map<Character, Integer> tMap = buildDict(t);
        Map<Character, Integer> sMap = new HashMap<>();
        char[] sChar = s.toCharArray();
        String result = "";
        boolean contains = false;
        while (true) {
            if (right > s.length() - 1 || left > s.length() - 1) {
                break;
            }
            if (!contains) {
                if (sMap.containsKey(sChar[right])) {
                    Integer i = sMap.get(sChar[right]);
                    i += 1;
                    sMap.put(sChar[right], i);
                } else {
                    sMap.put(sChar[right], 1);
                }
            }

            if (checkContains(sMap, tMap)) {
                if (result == "") {
                    result = s.substring(left , right+1);
                } else {
                    result = result.length() < (right - left + 1) ? result : s.substring(left , right+1);
                }

                Integer i = sMap.get(sChar[left]);
                i -= 1;
                sMap.put(sChar[left], i);
                left += 1;
                contains = true;
            } else {
                right += 1;
                contains = false;
            }


        }
        return result;
    }

    public boolean checkContains(Map<Character, Integer> ori, Map<Character, Integer> tar) {
        for (Map.Entry<Character, Integer> oneTar : tar.entrySet()) {
            Character targetAlphabet = oneTar.getKey();
            if (ori.get(targetAlphabet) == null || ori.get(targetAlphabet) < oneTar.getValue()) {
                return false;
            }
        }
        return true;
    }

    public Map<Character, Integer> buildDict(String t) {
        char[] array = t.toCharArray();
        Map<Character, Integer> result = new HashMap<>();
        for (char a : array) {
            if (result.containsKey(a)) {
                Integer i = result.get(a);
                i += 1;
                result.put(a, i);
            } else {
                result.put(a, 1);
            }
        }
        return result;
    }
}

【leetcode.15 三数之和】

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

这个题是三个数,确定一个数,另外两个就可以是一个双指针了

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Set<ThreeHolder> set = new HashSet<>();
        Arrays.sort(nums);
        int i = 0;
        List<List<Integer>> result = new ArrayList<>();
        while (i < nums.length && nums[i] <= 0) {
            int j = i + 1, k = nums.length - 1;
            while (j < k) {
                if (nums[j] + nums[k] > -1 * nums[i]) {
                    k -= 1;
                } else if (nums[j] + nums[k] < -1 * nums[i]) {
                    j += 1;
                } else {
                    ThreeHolder th = new ThreeHolder(nums[i], nums[j], nums[k]);
                    if (set.add(th)) {
                        result.add(th.toList());
                    }
                    k -= 1;
                    j += 1;
                }
            }
            i += 1;
        }
        return result;

    }
    
    public class ThreeHolder {
        private int num1;
        private int num2;
        private int num3;

        public ThreeHolder(int num1, int num2, int num3) {
            this.num1 = num1;
            this.num2 = num2;
            this.num3 = num3;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || o.getClass() != getClass()) {
                return false;
            }
            ThreeHolder that = (ThreeHolder) o;
            return num1 == that.num1 && num2 == that.num2 && num3 == that.num3;
        }

        @Override
        public int hashCode() {
            return Objects.hash(num1, num2, num3);
        }

        public List<Integer> toList() {
            return new ArrayList<>() {{
                add(num1);
                add(num2);
                add(num3);
            }};
        }
    }

}

【leetcode.209 长度最小的子数组】

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

此题是滑窗,本质其实还是双指针

有两个思路:

  • 像11题容器一样,从头尾开始缩小,谁小缩谁,但是这么做发现要用递归,而且这个题是把端点值算进去的,因此当两边相等的时候,要双判,增加难度

  • 思路2就是从小到大,如果大于等于target,移动end扩大窗口,否则移动start缩小窗口,这就是滑窗的思路

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int start = 0, end = 0;
        int minSize = Integer.MAX_VALUE;
        int current = nums[0];
        while (start < nums.length && end < nums.length) {
            if (current >= target) {
                minSize = Math.min(minSize, end - start + 1);
                if (start < end) {
                    current -= nums[start];
                    start += 1;
                } else {
                    current -= nums[start];
                    start += 1;
                    end += 1;
                    if (end < nums.length) {
                        current += nums[end];
                    }
                }                
            } else {
                end += 1;
                if (end < nums.length) {
                    current += nums[end];
                }
                
            }
        }
        if  (minSize == Integer.MAX_VALUE) {
            return 0;
        }
        return minSize;

    }

}

0

评论区