力扣100题解及笔记 双指针

 目录

1.力扣100题解及笔记 哈希-CSDN博客

2.力扣100题解及笔记 堆-CSDN博客

3.力扣100题解及笔记 栈-CSDN博客

4.力扣100题解及笔记 双指针-CSDN博客

5.力扣100题解及笔记 链表-CSDN博客

6.力扣100题解及笔记 二叉树-CSDN博客

7.力扣100题解及笔记 二分查找-CSDN博客

8.力扣100及题解 滑动窗口&子串-CSDN博客

9.力扣100题解及笔记 回溯-CSDN博客

10.力扣100题解及笔记 dp&多维dp-CSDN博客

11.力扣100题解及笔记 贪心-CSDN博客

12.力扣100题解及笔记 数组-CSDN博客

13.力扣100题解及笔记 技巧-CSDN博客

14.力扣100题解及笔记 矩阵-CSDN博客

15.力扣100题解及笔记 图论-CSDN博客

理论基础

1. 对撞指针

  • 指针从数组或序列的两端向中间移动。
  • 一般用于涉及数组中的元素两两配对,求和、判断条件或寻找某些特定的组合。
  • 典型问题:
    • 两数之和 II(有序数组):在有序数组中找到两个数,使它们的和为给定值。
    • 验证回文字符串:通过前后指针向中间移动,比较字符来判断是否为回文。

2. 快慢指针

  • 一个指针移动得快(通常一次两步),另一个指针移动得慢(通常一次一步)。
  • 用于检测链表中的循环、寻找链表的中间节点等。
  • 典型问题:
    • 判断链表是否有环:通过快慢指针,快指针比慢指针快两倍,若链表有环,两者最终会相遇。
    • 寻找链表的中间节点:当快指针到达末尾时,慢指针正好处于链表中间。

3. 左右边界法

  • 双指针分别从左右两端开始,根据某种条件调整左指针或右指针的位置,通常是为了找极值。
  • 适用于需要动态更新区间边界的问题,常用于解决动态最大值或最小值问题。
  • 典型问题:
    • 接雨水问题:用双指针分别从数组的两端向中间移动,根据较小的边界来决定能容纳多少雨水。
    • 盛最多水的容器:通过调整左、右指针,找到能够容纳最多水的两个柱子。

4. 滑动窗口

  • 双指针同时从一端出发,一个指针作为窗口的左边界,另一个指针作为右边界,动态扩大或缩小窗口。
  • 适用于求子数组或子串的最大值、最小值或满足某些条件的长度等。
  • 典型问题
    • 最长无重复子串:通过滑动窗口动态维护一个无重复字符的子串,并记录其最大长度。
    • 最小覆盖子串:找到包含给定字符串中所有字符的最小子串。

5. 归并场景

  • 指针分别指向两个已经排序的数组,通过移动指针来合并两个有序数组或链表。
  • 常用于排序或者合并有序序列的问题。
  • 典型问题
    • 合并两个有序数组:利用双指针将两个有序数组归并为一个新的有序数组。

双指针法的优势在效率上:通过两个指针在一个for循环下完成两个for循环的工作

可用于哈希、数组、数组、链表等多种结构

*官方归纳的双指针篇基本都是对撞指针和左右边界指针

283.移动零

283. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

 

示例 1:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:

输入: nums = [0]
输出: [0]
 

提示:

1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
 

进阶:你能尽量减少完成的操作次数吗?
class Solution {
    public void moveZeroes(int[] n) {
        if(n==null) return;
        int i,j=0;
        for(i=0;i<n.length;i++){//第三部分++i;i++没影响
            if(n[i]!=0) n[j++]=n[i];
        }
        for(i=j;i<n.length;++i){
            n[i]=0;
        }
    }
}

11.盛水最多

11. 盛最多水的容器

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

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

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

说明:你不能倾斜容器。
输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:

输入:height = [1,1]
输出:1
 

提示:

n == height.length
2 <= n <= 105
0 <= height[i] <= 104

可证明:将高度小的指针往内移动,才会枚举到更大的面积

(移动较短边是为了找到更高的边,增大容量,而移动较长边只会减少宽度,无法增加容量,所以不会漏掉最大值)

//简化版本
class Solution {
    public int maxArea(int[] h) {
        int i=0,j=h.length-1,r=0;
        while(i<j){
            r=h[i]<h[j] ? Math.max(r,(j-i)*h[i++]):Math.max(r,(j-i)*h[j--]);
        }
    return r;
    }
}


class Solution {
    public int maxArea(int[] h) {
        int i=0,j=h.length-1,r=0;
        while(i<j){
            int a;
            if(h[i]<h[j]) a=(j-i)*h[i++];
            else a=(j-i)*h[j--];
            r=Math.max(r,a);
        }
    return r;
    }
}

15.三数之和

15. 三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != 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] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。
示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。
 

提示:

3 <= nums.length <= 3000
-105 <= nums[i] <= 105
class Solution {
    public static List<List<Integer>> threeSum(int[] n) {
        List<List<Integer>> r = new ArrayList<>();
        int l = n.length;
        if (n == null || l < 3) return r; 
        Arrays.sort(n);
        for (int i = 0; i < l; i++) {
            if (n[i] > 0) break;//剪枝
            if (i > 0 && n[i] == n[i - 1]) continue;// 去重
            int L = i + 1, R = l - 1; 
            while (L < R) {
                int sum = n[i] + n[L] + n[R];
                if (sum == 0) {
                    r.add(Arrays.asList(n[i], n[L], n[R]));//例如[[-1, -1, 2], [-1, 0, 1]]
                    // 去重,并移动左右指针
                    while (L < R && n[L] == n[L + 1]) L++;
                    while (L < R && n[R] == n[R - 1]) R--;
                    L++;
                    R--;
                } else if (sum < 0) L++;
                else R--;
            }
        }
        return r;
    }
}

42.接雨水h

42. 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

 

示例 1:



输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 
示例 2:

输入:height = [4,2,0,3,2,5]
输出:9
 

提示:

n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105

class Solution {
    public int trap(int[] h) { 
        int r=0,L=0,R=h.length-1,pm=0,am=0,tmp;
        //pm左侧的最高柱子,am右侧的最高柱子
        while (L < R) {
            pm=Math.max(pm, h[L]);
            am = Math.max(am, h[R]);
            tmp = pm < am ? pm - h[L++] : am - h[R--]; //计算左右柱子能接的水量,左右两侧最高中更小决定当前的水量
            r=r+tmp;
        }
        return r;
    }
}

单调栈法

class Solution {
    public int trap(int[] h) {
        int r = 0;
        Deque<Integer> d = new ArrayDeque<>();  // 用栈存放柱子的索引
        for (int i = 0; i < h.length; i++) {
            while (!d.isEmpty() && h[i] >= h[d.peek()]) {
                int j= h[d.pop()];  // 栈顶元素表示低洼处的柱子高度
                if (d.isEmpty())  break;
                int left = d.peek();
                int dh = Math.min(h[left], h[i]) - j; // 计算高度差
                r += dh * (i - left - 1);  // 计算接雨水的面积并累加
            }
            d.push(i);  // 将当前柱子的索引入栈
        }
        return r;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值