【LeetCode精选算法】双指针专题一

目录

[1. 移动零(283. Move Zeroes)](#1. 移动零(283. Move Zeroes))

[2. 复写零(1089. Duplicate Zeros)](#2. 复写零(1089. Duplicate Zeros))

[3. 快乐数(202. Happy Number)](#3. 快乐数(202. Happy Number))

[4. 盛水最多的容器(11. Container With Most Water)](#4. 盛水最多的容器(11. Container With Most Water))


1. 移动零(283. Move Zeroes)

题目链接LeetCode 283

详细解题思路

  1. 核心思想:采用双指针法,一个指针用于遍历数组,另一个指针用于记录非零元素的最终位置。

  2. 具体步骤

    • 初始化dest = -1,表示当前已处理的非零元素序列的末尾(从-1开始表示还未处理任何非零元素)

    • 遍历数组,使用cur指针从0开始扫描每个元素

    • nums[cur] != 0时:

      • dest指针先右移一位(dest++

      • 交换nums[dest]nums[cur]的值

      • 这样保证[0, dest]区间内都是非零元素

    • nums[cur] == 0时,不做任何操作,继续遍历

  3. 时间复杂度:O(n),只需一次遍历

  4. 空间复杂度:O(1),原地修改

java 复制代码
class Solution {
    public void moveZeroes(int[] nums) {
        // dest指向已处理的非零元素序列的末尾
        int dest = -1;
        for (int cur = 0; cur < nums.length; cur++) {
            if (nums[cur] != 0) {
                // 发现非零元素,dest先右移
                dest++;
                // 交换dest和cur位置的元素
                int tmp = nums[dest];
                nums[dest] = nums[cur];
                nums[cur] = tmp;
            }
        }
    }
}

2. 复写零(1089. Duplicate Zeros)

题目链接LeetCode 1089

详细解题思路

  1. 核心思想:双指针从后向前复写,避免覆盖问题

  2. 具体步骤

    • 第一步:找到最后一个需要保留的元素

      • 使用两个指针curdestcur遍历原数组,dest跟踪复写后的位置

      • 遇到0时dest += 2,遇到非0时dest += 1

      • dest >= n-1时停止,cur就是最后一个需要保留的元素

    • 第二步:处理边界情况

      • 如果dest == n,说明最后一个0复写后刚好超出数组,需要特殊处理
    • 第三步:从后向前复写

      • cur位置开始向前遍历

      • 遇到0时,复写两次(注意边界)

      • 遇到非0时,复写一次

  3. 关键点:从后向前复写避免元素被覆盖,先找到"终点"再倒推

  4. 时间复杂度:O(n),两次遍历

java 复制代码
class Solution {
    public void duplicateZeros(int[] arr) {
        int n = arr.length;
        int cur = 0, dest = -1;
        
        // 1. 找到最后一个需要保留的元素
        while (cur < n) {
            if (arr[cur] == 0) dest += 2;
            else dest++;
            
            if (dest >= n - 1) break;
            cur++;
        }
        
        // 2. 处理边界情况:最后一个0复写后超出数组
        if (dest == n) {
            arr[n - 1] = 0;
            cur--;
            dest -= 2;
        }
        
        // 3. 从后向前复写
        while (cur >= 0) {
            if (arr[cur] == 0) {
                // 复写0两次
                if (dest >= 0) arr[dest--] = 0;
                if (dest >= 0) arr[dest--] = 0;
            } else {
                // 复写非0一次
                if (dest >= 0) arr[dest--] = arr[cur];
            }
            cur--;
        }
    }
}

3. 快乐数(202. Happy Number)

题目链接LeetCode 202

详细解题思路

  1. 核心思想:快慢指针检测循环,类似链表检测环

  2. 数学原理

    • 对于任何正整数,经过"各位平方和"变换,最终必然进入循环

    • 鸽巢原理:变换值在[1, 810]范围内,最多变换811次必出现重复

  3. 具体步骤

    • 初始化慢指针slow = n,快指针fast = bitSum(n)

    • slow != fast时循环:

      • slow计算一次变换

      • fast计算两次变换

    • 如果最终相遇在1,则是快乐数;否则不是

  4. 辅助函数bitSum计算各位平方和

  5. 时间复杂度:O(log n),空间复杂度:O(1)

java 复制代码
class Solution {
    public boolean isHappy(int n) {
        int slow = n, fast = bitSum(n);
        
        // 快慢指针,快指针速度是慢指针的两倍
        while (slow != fast) {
            slow = bitSum(slow);          // 慢指针走一步
            fast = bitSum(bitSum(fast));  // 快指针走两步
        }
        
        // 相遇点是否为1
        return slow == 1;
    }
    
    // 计算数字n的各位平方和
    private int bitSum(int n) {
        int sum = 0;
        while (n != 0) {
            int t = n % 10;  // 取个位
            sum += t * t;
            n /= 10;         // 去掉个位
        }
        return sum;
    }
}

4. 盛水最多的容器(11. Container With Most Water)

题目链接LeetCode 11

详细解题思路

  1. 核心思想:对撞指针,每次移动较短的那一边

  2. 容积公式v = min(height[left], height[right]) * (right - left)

  3. 移动策略证明

    • 假设左边界高度 < 右边界高度

    • 如果移动较高的右边,容积一定减少(宽度减少,高度不会增加)

    • 如果移动较低的左边,容积可能增加(高度可能增加)

    • 因此每次都移动较短的那一边

  4. 具体步骤

    • 初始化left=0, right=n-1

    • left < right时循环:

      • 计算当前容积并更新最大值

      • 移动高度较小的指针

  5. 时间复杂度:O(n),空间复杂度:O(1)

java 复制代码
class Solution {
    public int maxArea(int[] height) {
        int left = 0, right = height.length - 1;
        int maxWater = 0;
        
        while (left < right) {
            // 计算当前容器的水量
            int h = Math.min(height[left], height[right]);
            int w = right - left;
            int water = h * w;
            
            // 更新最大水量
            maxWater = Math.max(maxWater, water);
            
            // 移动较短的那一边
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        
        return maxWater;
    }
}
相关推荐
未来龙皇小蓝2 小时前
策略模式:Spring Bean策略与枚举 Lambda策略
java·windows·spring boot·spring·策略模式
多米Domi0112 小时前
0x3f 第36天 外卖8,9,树
数据结构·python·算法·leetcode
jonyleek2 小时前
开源APS排产系统,出货计划如何成为企业降本增效的关键?
算法·开源·私有化部署·软件开发·生产排产·aps排产系统
hetao17338372 小时前
2026-01-16~19 hetao1733837 的刷题笔记
c++·笔记·算法
LiRuiJie2 小时前
从OS层面深入剖析JVM如何实现多线程与同步互斥
java·jvm·os·底层
m0_719084112 小时前
滴滴滴滴滴
java·开发语言
程序员-King.2 小时前
day153—回溯—子集(LeetCode-78)
算法·leetcode·回溯·递归
MicroTech20252 小时前
突破C2Q瓶颈,MLGO微算法科技高性能可重构计算机实现量子算法真实级仿真,推动量子仿真进入新阶段
科技·算法·重构
张乔242 小时前
spring boot项目中设置默认的方法实现
java·数据库·spring boot