【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;
    }
}
相关推荐
jxy99989 小时前
mac mini 安装java JDK 17
java·开发语言·macos
YGGP9 小时前
【Golang】LeetCode 1. 两数之和
leetcode
唐梓航-求职中9 小时前
编程大师-技术-算法-leetcode-355. 设计推特
算法·leetcode·面试
biyezuopinvip9 小时前
基于Spring Boot的企业网盘的设计与实现(毕业论文)
java·spring boot·vue·毕业设计·论文·毕业论文·企业网盘的设计与实现
Hx_Ma169 小时前
SSM搭建(三)Spring整合SpringMVC框架
java·后端·spring
无风听海9 小时前
.NET10之ASP.NET Core的Filter管线
java·asp.net·.net
少许极端9 小时前
算法奇妙屋(二十八)-递归、回溯与剪枝的综合问题 1
java·算法·深度优先·剪枝·回溯·递归
仰泳的熊猫9 小时前
题目1453:蓝桥杯历届试题-翻硬币
数据结构·c++·算法·蓝桥杯
唐梓航-求职中9 小时前
技术-算法-leetcode-1606. 找到处理最多请求的服务器(易懂版)
服务器·算法·leetcode
Boop_wu9 小时前
简单介绍 JSON
java·开发语言