【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. 具体步骤:

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

      • 使用两个指针cur和dest,cur遍历原数组,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;
    }
}
相关推荐
丘山望岳1 小时前
单调栈和单调队列
数据结构
Fuyo_11191 小时前
C++ AVL树保姆级详解:从平衡因子到四大旋转机制
java·c++·算法
Sarvartha1 小时前
Date、LocalDate、LocalTime、LocalDateTime、DateTimeFormatter类
java
jktantan1 小时前
每个节点 41 字节:把一棵百万节点树拆进 14 个 typed array
前端·javascript·vue.js·算法·typescript
阿俊-全栈开发2 小时前
LikeShop 多商户 Java 版商城微服务模块拆分与部署架构演进实践
java·微服务·架构·likeshop开源商城·多商户源码
揽星河..2 小时前
【C语言】猜数字游戏
c语言·算法·游戏
Wang's Blog2 小时前
Java 项目实战:交友项目 - 联系人列表
java·开发语言·交友
我命由我123452 小时前
访客系统形式(有用户概念、无用户概念)
java·开发语言·前端·javascript·python·产品运营·产品经理
LabVIEW开发2 小时前
CTM-3 红外测温仪 + LabVIEW 温度怎么测?
算法·labview·测试·labview程序
纪念 2293 小时前
C++算法(六)
开发语言·c++·算法