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)

相关推荐
iAkuya3 小时前
(leetcode)力扣100 58组合总和(回溯)
算法·leetcode·职场和发展
80530单词突击赢3 小时前
C++关联容器深度解析:set/map全攻略
java·数据结构·算法
m0_561359673 小时前
代码热更新技术
开发语言·c++·算法
_F_y4 小时前
链表:重排链表、合并 K 个升序链表、K 个一组翻转链表
数据结构·链表
xu_yule4 小时前
算法基础—组合数学
c++·算法
爱尔兰极光4 小时前
LeetCode--移除元素
算法·leetcode·职场和发展
XLYcmy4 小时前
一个用于统计文本文件行数的Python实用工具脚本
开发语言·数据结构·windows·python·开发工具·数据处理·源代码
方便面不加香菜4 小时前
数据结构--链式结构二叉树
c语言·数据结构
senijusene4 小时前
数据结构:单向链表(2)以及双向链表
数据结构·链表