LeetCode---基础算法刷题指南

双指针算法

一、什么是双指针算法?

双指针算法是一种通过使用两个指针(通常指向数组的不同位置)来高效解决问题的技巧。它主要用于:

  • 减少时间复杂度(从 O(n²) 降到 O(n))

  • 优化空间复杂度(常为 O(1))

  • 处理有序或部分有序的数据

二、题目详解与个人思路

1. 283. 移动零

题目要求:将数组中的所有 0 移动到末尾,保持非零元素的相对顺序。

我的思路

  • 使用快慢指针:left指向已处理区域的尾部,right指向未处理的头部

  • right遇到非零元素时,与left交换,然后left后移

  • 这样可以保证所有非零元素都被移到前面,且顺序不变

cpp 复制代码
void moveZeroes(vector<int>& nums) {
    if (nums.size() <= 1) return;
    int left = 0, right = 0;
    while(right <= nums.size()-1) {
        if(nums[right] != 0) {
            swap(nums[left++], nums[right]);
        }
        right++;
    }
}
// 时间复杂度:O(n) - 每个元素最多被访问一次
// 空间复杂度:O(1) - 只使用了常数个额外变量

2. 1089. 复写零

题目要求:遇到 0 就复写一次,数组长度不变,从末尾开始覆盖。

我的思路

  • 难点:原地修改,不能使用额外数组

  • 关键:先确定哪些元素会被保留

  • 步骤:

    1. 使用curdest指针模拟复写过程

    2. 第一次遍历确定最终保留的元素位置

    3. 从后向前进行实际复写操作

cpp 复制代码
void duplicateZeros(vector<int>& arr) {
    int cur = 0, dest = 0;
    // 确定最后保留的元素
    for(int i = 0; i < arr.size()-1; i++) {
        if(arr[i] != 0) dest++;
        else dest += 2;
        cur++;
        if(dest >= arr.size()) break;
    }
    
    // 从后向前复写
    while(cur >= 1) {
        if(dest > arr.size()) {
            dest = arr.size();
            arr[dest-1] = 0;
            cur--;
            dest--;
        } else {
            if(arr[cur-1] == 0) {
                arr[dest-1] = 0;
                arr[dest-2] = 0;
                cur--;
                dest -= 2;
            } else {
                arr[dest-1] = arr[cur-1];
                cur--;
                dest--;
            }
        }
    }
}
// 时间复杂度:O(n) - 两次遍历,第一次确定保留元素,第二次实际复写
// 空间复杂度:O(1) - 原地修改,只使用常数空间

3. 202. 快乐数

题目要求:判断一个数是否为快乐数(各位平方和最终变为1)。

我的思路

  • 核心问题:如何判断是否进入无限循环?

  • 解决方案:快慢指针法(类似链表判断环)

  • 快指针每次计算两次,慢指针计算一次

  • 相遇时判断值是否为1

cpp 复制代码
int bitSquareSum(int n) {
    int sum = 0;
    while(n > 0) {
        int bit = n % 10;
        sum += bit * bit;
        n = n / 10;
    }
    return sum;
}

bool isHappy(int n) {
    int slow = n, fast = n;
    do {
        slow = bitSquareSum(slow);
        fast = bitSquareSum(fast);
        fast = bitSquareSum(fast);
    } while(slow != fast);
    return slow == 1;
}
// 时间复杂度:O(k) - k为循环次数,最坏情况下可能达到循环上限
// 空间复杂度:O(1) - 只使用了常数个额外变量
// 分析:使用Floyd判圈算法,无需哈希表存储已访问数字

4. 11. 盛最多水的容器

题目要求:找到两条线,使它们与 x 轴构成的容器能容纳最多的水。

我的思路

2. 左右指针

3. 滑动窗口

4. 中心扩展

四、常见易错点

  • 双指针从两端向中间移动

  • 容量 = 宽度 × 最小高度

  • 每次移动高度较小的指针(因为容量受限于最小高度)

  • 记录最大容量

    cpp 复制代码
    int maxArea(vector<int>& height) {
        int max_cap = 0, w = 0, h = 0;
        int left = 0, right = height.size()-1;
        while(left < right) {
            h = min(height[left], height[right]);
            w = right - left;
            max_cap = max(max_cap, w * h);
            height[left] > height[right] ? right-- : left++;
        }
        return max_cap;
    }
    // 时间复杂度:O(n) - 双指针从两端向中间遍历一次
    // 空间复杂度:O(1) - 只使用了常数个额外变量

    5. 15. 三数之和

    题目要求:找到数组中所有和为 0 的不重复三元组。

    我的思路

  • 先排序(O(nlogn))

  • 固定一个数,转化为两数之和问题

  • 使用双指针寻找另外两个数

  • 关键:去重处理非常重要

    cpp 复制代码
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        
        for(int i = 0; i < nums.size()-2; i++) {
            // 去重:跳过相同的第一个数
            if(i > 0 && nums[i] == nums[i-1]) continue;
            
            int target = -nums[i];
            int left = i+1, right = nums.size()-1;
            
            while(left < right) {
                int sum = nums[left] + nums[right];
                if(sum < target) left++;
                else if(sum > target) right--;
                else {
                    result.push_back({nums[i], nums[left], nums[right]});
                    
                    // 去重:跳过相同的左右指针值
                    while(left < right && nums[left] == nums[left+1]) left++;
                    while(left < right && nums[right] == nums[right-1]) right--;
                    left++; right--;
                }
            }
        }
        return result;
    }
    // 时间复杂度:O(n²) 
    //   - 排序:O(nlogn)
    //   - 外层循环:O(n)
    //   - 内层双指针:O(n)
    //   - 总复杂度:O(nlogn) + O(n²) = O(n²)
    // 空间复杂度:O(1) 或 O(n)(取决于排序算法)
    //   - 如果排序是原地排序:O(1)
    //   - 如果排序需要额外空间:O(n)

    6. 18. 四数之和

    题目要求:找到数组中所有和为 target 的不重复四元组。

    我的思路

  • 扩展三数之和的思路

  • 双重循环固定前两个数

  • 内层使用双指针寻找后两个数

  • 注意整型溢出问题(使用long long)

    cpp 复制代码
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<vector<int>> result;
        
        for(int i = 0; i < n-3;) {
            for(int j = i+1; j < n-2;) {
                long long task = (long long)target - nums[i] - nums[j];
                int left = j+1, right = n-1;
                
                while(left < right) {
                    int sum = nums[left] + nums[right];
                    if(sum > task) right--;
                    else if(sum < task) left++;
                    else {
                        result.push_back({nums[i], nums[j], nums[left++], nums[right--]});
                        while(left < right && nums[left] == nums[left-1]) left++;
                        while(left < right && nums[right] == nums[right+1]) right--;
                    }
                }
                j++;
                while(j < n-2 && nums[j] == nums[j-1]) j++;
            }
            i++;
            while(i < n-3 && nums[i] == nums[i-1]) i++;
        }
        return result;
    }
    // 时间复杂度:O(n³)
    //   - 排序:O(nlogn)
    //   - 第一层循环:O(n)
    //   - 第二层循环:O(n)
    //   - 内层双指针:O(n)
    //   - 总复杂度:O(nlogn) + O(n³) = O(n³)
    // 空间复杂度:O(1) 或 O(n)(取决于排序算法)
    //   - 输出结果的空间不算在空间复杂度内(题目要求)

    三、双指针技巧总结

    1. 快慢指针

  • 应用场景:链表环检测、数组原地修改

  • 经典题目:移动零、复写零、快乐数

  • 应用场景:有序数组、回文判断

  • 经典题目:两数之和、三数之和、盛水容器

  • 应用场景:子串、子数组问题

  • 特点:同向移动的双指针

  • 应用场景:回文子串

  • 特点:从中心向两边扩展

  • 边界条件:双指针移动时要确保不越界

  • 去重处理:多指针问题中跳过重复元素很重要

  • 指针更新顺序:先计算结果再移动指针,避免错过解

  • 时间复杂度:双指针通常将 O(n²) 优化为 O(n)

相关推荐
一切皆是因缘际会6 分钟前
因果推理人工智能
大数据·数据结构·人工智能
好评笔记9 分钟前
深度学习面试八股—— GRU(Gated Recurrent Unit)
人工智能·rnn·深度学习·算法·机器学习·gru·校招
搞科研的小刘选手14 分钟前
【智能计算方向专题研讨会】第三届智能计算与数据分析国际学术会议(ICDA 2026)
大数据·算法·机器学习·数据挖掘·数据分析·可视化·计算
量化君也20 分钟前
桥水基金全天候策略拆解,构建中国ETF躺平版策略
大数据·人工智能·python·算法·金融·业界资讯
蓦然回首却已人去楼空28 分钟前
画图专用文档
算法
洛水水30 分钟前
【力扣100题】78.在排序数组中查找元素的第一个和最后一个位置
数据结构·算法·leetcode
江屿风31 分钟前
C++图论基础拓扑排序算法流食般投喂
开发语言·c++·笔记·算法·排序算法
海棠AI实验室39 分钟前
AI 时代文献综述:从检索到成稿的 RAG 五步法
windows·算法·自动化·llm·rag
H1785350909639 分钟前
SolidWorks_基于草图的实体特征14_扫描扭转与控制
前端·人工智能·算法·3d建模·solidworks
黄金龙PLUS41 分钟前
基于ARX结构的新型序列密码算法FlashLight
算法·网络安全·密码学·哈希算法·同态加密