双指针算法(一)

目录

[🔹 移动零(283)](#🔹 移动零(283))

题目要求

核心思路

代码实现

探索思考

[🔹 复写零(1089)](#🔹 复写零(1089))

题目要求

核心思路

代码实现

探索思考

[🔹 快乐数(202)](#🔹 快乐数(202))

题目要求

核心思路

代码实现

探索思考

[💡 刷题总结](#💡 刷题总结)


最近刷了三道经典的算法题,分别是「移动零」「复写零」和「快乐数」,每一道都有巧妙的解题思路。我想把这些思路和对应的代码实现记录下来,也算是一次探索性学习的总结。


🔹 移动零(283)

题目要求

把数组里所有的 0 移动到末尾,同时保持非零元素的相对顺序,必须原地操作

核心思路

这是一个典型的双指针问题:

  • left 指针:指向当前可以放置非零元素的位置(初始为 -1
  • right 指针:遍历整个数组
  • right 遇到非零元素时,就和 left+1 位置的元素交换,这样保证了非零元素的顺序,也把 0 挤到了后面

代码实现

cpp 复制代码
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int left = -1, right = 0;
        while (right < nums.size()) {
            if (nums[right] == 0) {
                right++;
            } else {
                swap(nums[++left], nums[right++]);
            }
        }
    }
};

探索思考

这道题的关键是理解 "原地操作" 的含义。如果允许使用额外数组,直接把非零元素存进去再补零就可以了,但双指针的方法让空间复杂度降到了 O(1),是最优解。


🔹 复写零(1089)

题目要求

遍历数组,遇到 0 就把它复写一遍,后面的元素右移,且不能超过数组长度

核心思路

这道题的难点在于如果直接从前往后复写,会覆盖掉还没处理的元素。所以我们用两次遍历的思路:

  1. 第一次遍历 :用 curdest 两个指针,计算出最终每个元素的目标位置,找到最后一个需要保留的元素。
  2. 第二次遍历 :从后往前复写,这样就不会覆盖未处理的元素。如果最后一个元素是 0 且刚好超出数组长度,需要单独处理。

代码实现

cpp 复制代码
class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int cur = 0, dest = -1;
        int n = arr.size();
        // 第一次遍历:找到最终的位置
        while (cur < n) {
            if (arr[cur]) dest++;
            else dest += 2;
            if (dest >= n - 1) break;
            cur++;
        }
        // 处理边界情况:最后一个0复写后超出数组
        if (dest == n) {
            arr[n - 1] = 0;
            cur--;
            dest -= 2;
        }
        // 第二次遍历:从后往前复写
        while (cur >= 0) {
            if (arr[cur]) {
                arr[dest--] = arr[cur--];
            } else {
                arr[dest--] = 0;
                arr[dest--] = 0;
                cur--;
            }
        }
    }
};

探索思考

从后往前的逆向思维是这道题的精髓。如果正向操作,每次遇到 0 都要移动后面的元素,时间复杂度会达到 O(n²),而两次遍历的方法把时间复杂度降到了 O(n),非常高效。


🔹 快乐数(202)

题目要求

判断一个数是不是快乐数:反复计算各位数字的平方和,最终如果能得到 1 就是快乐数,否则会进入无限循环。

核心思路

这道题的关键是如何判断 "无限循环"。我们可以用 ** 快慢指针(Floyd 判圈算法)** 来检测循环:

  • slow 指针:每次计算一次平方和
  • fast 指针:每次计算两次平方和
  • 如果存在循环,fast 最终会追上 slow;如果是快乐数,slow 会先到达 1

代码实现

cpp 复制代码
class Solution {
public:
    int bitsum(int n) {
        int sum = 0;
        while (n) {
            sum += pow(n % 10, 2);
            n = n / 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int slow = n, fast = bitsum(n);
        while (slow != fast) {
            slow = bitsum(slow);
            fast = bitsum(bitsum(fast));
        }
        return slow == 1;
    }
};

探索思考

这道题也可以用哈希表来记录已经出现过的数字,但快慢指针的方法不需要额外空间,空间复杂度是 O(1)。而且判圈算法在很多链表问题中也会用到,是一个非常通用的技巧。


💡 刷题总结

这三道题虽然看起来不同,但都用到了双指针的核心思想:

  • 移动零:同向双指针,一个负责找非零元素,一个负责放置
  • 复写零:两次遍历,正向找位置,逆向写元素
  • 快乐数:快慢指针,检测循环

探索性学习的乐趣就在于,你会发现很多看似无关的问题,背后都有相通的解题思路。多总结、多思考,才能真正把算法内化成自己的能力。

相关推荐
月挽清风16 小时前
代码随想录第35天:动态规划
算法·动态规划
岛雨QA16 小时前
链表「Java数据结构与算法学习笔记3」
数据结构·算法
Sunsets_Red17 小时前
P8277 [USACO22OPEN] Up Down Subsequence P 题解
c语言·c++·算法·c#·学习方法·洛谷·信息学竞赛
菜鸟小九17 小时前
redis原理篇(基本数据结构)
数据结构·数据库·redis
汉克老师17 小时前
GESP2023年12月认证C++二级( 第三部分编程题(2) 小杨的H字矩阵)
c++·算法·矩阵·循环结构·gesp二级·gesp2级
_Li.17 小时前
Simulink-螺旋桨动力模块
人工智能·算法·机器学习
Charlie_lll17 小时前
力扣解题-438. 找到字符串中所有字母异位词
后端·算法·leetcode
奶茶树17 小时前
【数据结构】红黑树
数据结构·c++·算法
AI周红伟17 小时前
周红伟:OpenAI 首席运营官,尚未真正看到人工智能渗透到企业业务流程中
人工智能·算法·性能优化
问好眼17 小时前
《算法竞赛进阶指南》0x01 位运算-2.增加模数
c++·算法·位运算·信息学奥赛