双指针,数组去重

一、核心原理

  1. 慢指针(i) :指向去重后新数组的最后一个有效位置

  2. 快指针(j) :遍历整个原数组,寻找新的不重复元素

  3. 规则:

    • 找到不重复元素 → 赋值给慢指针的下一位,慢指针前进。

    • 找到重复元素 → 快指针直接跳过。


二、场景 1:有序数组去重(保留一个重复元素)

题目要求:

给定升序有序数组 ,原地删除重复元素,使每个元素只出现一次,返回新数组长度。

示例:[0,0,1,1,1,2,2,3,3,4] → 去重后 [0,1,2,3,4],返回长度 5。

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;
​
// 有序数组去重,双指针核心函数
int removeDuplicates(vector<int>& nums) {
    // 边界:空数组直接返回 0
    if (nums.empty()) return 0;
​
    // 慢指针:初始指向第一个元素(新数组最后一位)
    int slow = 0;
    // 快指针:遍历整个数组
    for (int fast = 1; fast < nums.size(); fast++) {
        // 找到不重复的元素
        if (nums[fast] != nums[slow]) {
            slow++; // 慢指针前进
            nums[slow] = nums[fast]; // 覆盖更新
        }
        // 重复:快指针自动++,无需操作
    }
    // 新数组长度 = 慢指针下标 + 1
    return slow + 1;
}
​
int main() {
    vector<int> nums = {0, 0, 1, 1, 1, 2, 2, 3, 3, 4};
    int newLen = removeDuplicates(nums);
​
    cout << "去重后数组长度:" << newLen << endl;
    cout << "去重后数组:";
    for (int i = 0; i < newLen; i++) {
        cout << nums[i] << " ";
    }
    return 0;
}

三、场景 2:有序数组去重(保留最多两个重复元素)

题目要求

每个元素最多出现 2 次,LeetCode 80 经典题。

示例:[1,1,1,2,2,3][1,1,2,2,3],返回 5。

cpp 复制代码
int removeDuplicates2(vector<int>& nums) {
    if (nums.size() <= 2) return nums.size();
​
    int slow = 1; // 允许前两个元素保留
    for (int fast = 2; fast < nums.size(); fast++) {
        // 核心:和 slow-1 比较,保证最多两个重复
        if (nums[fast] != nums[slow - 1]) {
            slow++;
            nums[slow] = nums[fast];
        }
    }
    return slow + 1;
}

四、场景 3:无序数组去重(双指针通用版)

无序数组不能直接比较相邻元素,双指针 + 标记实现原地去重:

cpp 复制代码
int removeDuplicatesUnordered(vector<int>& nums) {
    if (nums.empty()) return 0;
​
    int slow = 0;
    for (int fast = 1; fast < nums.size(); fast++) {
        bool isDuplicate = false;
        // 检查 fast 是否在 slow 前面已出现
        for (int k = 0; k <= slow; k++) {
            if (nums[fast] == nums[k]) {
                isDuplicate = true;
                break;
            }
        }
        // 不重复则加入新数组
        if (!isDuplicate) {
            slow++;
            nums[slow] = nums[fast];
        }
    }
    return slow + 1;
}

建议用哈希表实现:O(n)时间

cpp 复制代码
int removeDuplicates(vector<int>& nums) {
    unordered_map<int, bool> mp;
    int idx = 0;
​
    for (int x : nums) { //遍历数组 /vector 等容器,不用写下标
        if (!mp[x]) {
            mp[x] = true;
            nums[idx++] = x;
        }
    }
​
    nums.resize(idx);
    return idx;
}

五、总结

慢指针slow: 标记去重数组的最后位置

快指针fast: 遍历数组,寻找新元素

O(n)时间 ,O(1)空间

相关推荐
深邃-15 小时前
【数据结构与算法】-二叉树(2):实现顺序结构二叉树(堆的实现),向上调整算法,向下调整算法,堆排序,TOP-K问题
数据结构·算法·二叉树·排序算法·堆排序··top-k
We་ct18 小时前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·javascript·算法·leetcode·typescript
王老师青少年编程1 天前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·合并果子
叼烟扛炮1 天前
C++第二讲:类和对象(上)
数据结构·c++·算法·类和对象·struct·实例化
天疆说1 天前
【哈密顿力学】深入解读航天器交会最优控制中的Hamilton函数
人工智能·算法·机器学习
wuweijianlove1 天前
关于算法设计中的代价函数优化与约束求解的技术7
算法
leoufung1 天前
LeetCode 149: Max Points on a Line - 解题思路详解
算法·leetcode·职场和发展
样例过了就是过了1 天前
LeetCode热题100 最长公共子序列
c++·算法·leetcode·动态规划
HXDGCL1 天前
矩形环形导轨:自动化循环线的核心运动单元解析
运维·算法·自动化
谭欣辰1 天前
C++ 排列组合完整指南
开发语言·c++·算法