【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;
    }
}
相关推荐
hansang_IR11 小时前
【讲解】CSP-S2026 第一轮初赛
c++·算法
毅炼11 小时前
不写多语言 SDK,怎么让 Python、Go、Node 服务接入注册中心?
java·后端·系统架构·gateway
hanlin0311 小时前
刷题笔记:力扣第76题-最小覆盖子串
笔记·算法·leetcode
Zane199411 小时前
比较排序跑不出O(nlogn)是数学上证明过的,计数、桶、基数排序凭什么能绕开这堵墙
算法·排序算法
tiger86511 小时前
大语言模型面试和题解,梳理中国主流开源 LLM 系列的发展脉络、技术路线与工程取舍
人工智能·gpt·深度学习·算法·自然语言处理·面试·transformer
2501_9378609411 小时前
上篇:网络编程基础与UDP套接字编程
java·网络·计算机网络
泡海椒11 小时前
jquick-pdf 表格行列尺寸控制实战:单元格合并的可行边界
java·开发语言·pdf
Wang's Blog11 小时前
Java 项目实战: 外卖平台-后台系统登录功能开发
java·服务器·redis
我不会起名字32211 小时前
一天一道力扣Hot100(36):深度优先算法---组合总和
数据结构·c++·后端·python·算法·go
海鸥-w11 小时前
spingboot定义一个全局异常处理器
java