数组去重进阶:一次遍历实现最多保留指定个数重复元素(O(n)时间+O(1)空间)

在数组操作场景中,"去重"是高频需求,而基础去重(完全保留不重复元素)之外,更常见的是"最多保留k个重复元素"的进阶需求。本文将详细讲解一种高效的原地去重思路,仅需一次遍历即可完成,时间复杂度O(n)、空间复杂度O(1),且能灵活扩展至任意保留个数k。

一、问题背景

给定一个有序/无序数组(注:该算法对数组是否有序无要求,核心靠"有效区域"判断重复),要求原地删除多余的重复元素,使得每个元素最多出现2次(可扩展至k次),返回处理后数组的长度,且处理后的元素需保持原数组的相对顺序。

核心约束:不使用额外辅助数组,仅在原数组上操作,追求最优时间和空间效率。

二、核心思路

该算法的核心是**"原地筛选+有效区域标记"**,通过一个变量标记已处理完成的有效区域,再通过简单规则判断当前元素是否能加入有效区域,全程无冗余操作。

1. 核心变量定义

用变量 k 标记"已处理完成的有效区域"的长度,本质是一个尾指针:

  • 初始时 k=0,表示有效区域为空(无任何处理完成的元素);

  • 遍历结束后,数组前 k 个元素即为符合要求的结果,k 就是结果数组的长度。

2. 遍历判断规则

从左到右遍历数组的每个元素 x,判断 x 是否能加入有效区域,规则如下:

  • 情况1:k < 2(有效区域长度不足2)

  • 直接将 x 放入 nums[k] 位置,然后 k++ 扩大有效区域。原因是"最多保留2个重复元素",前2个元素无论是否重复,都不会违反规则,无需额外判断。

  • 情况2:k ≥ 2(有效区域长度≥2)

  • 对比 x 与有效区域的"倒数第2个元素"(即 nums[k-2]):

    • x ≠ nums[k-2]:说明有效区域的最后两个元素中,没有和 x 连续重复超过2次的情况,可加入有效区域(nums[k] = xk++);

    • x = nums[k-2]:说明有效区域的最后两个元素已与 x 相同(如有效区域是 [1,1],x 也是1),再加入会出现3个连续重复,直接跳过 xk 保持不变。

3. 实例演示

以原数组 [1,1,1,2,2,3] 为例,一步步演示执行过程:

  1. 遍历第1个元素 1:k=0 < 2,放入 nums[0]k=1;有效区域:[1]

  2. 遍历第2个元素 1:k=1 < 2,放入 nums[1]k=2;有效区域:[1,1]

  3. 遍历第3个元素 1:k=2 ≥ 2,对比 nums[0] = 1x=1 相等,跳过;有效区域不变,k=2

  4. 遍历第4个元素 2:k=2 ≥ 2,对比 nums[0] = 1x=2 不相等,放入 nums[2]k=3;有效区域:[1,1,2]

  5. 遍历第5个元素 2:k=3 ≥ 2,对比 nums[1] = 1x=2 不相等,放入 nums[3]k=4;有效区域:[1,1,2,2]

  6. 遍历第6个元素 3:k=4 ≥ 2,对比 nums[2] = 2x=3 不相等,放入 nums[4]k=5;有效区域:[1,1,2,2,3]

遍历结束,结果数组为前5个元素 [1,1,2,2,3],长度 k=5,完全符合"最多保留2个重复元素"的要求。

三、扩展通用版:最多保留k个重复元素

基础版的思路可直接扩展至"最多保留任意k个重复元素",核心逻辑不变,仅需调整判断规则,通用性极强。

1. 通用核心逻辑

  • 前k个元素:直接保留,无需判断。因为"最多保留k个重复元素",前k个元素哪怕全重复,也不会违反规则;

  • 第k+1个元素及以后:判断当前元素 x 与"有效区域倒数第k个元素"(即 nums[k-1 - (k-1)] = nums[有效长度 - k])是否相同:

    • 不同:加入有效区域,有效长度(k)加1;

    • 相同:跳过,不加入有效区域。

2. 通用规则验证

以"最多保留3个重复元素"(k=3)为例,原数组 [2,2,2,2,3,3,3]

  • 前3个2:直接保留,有效长度k=3,有效区域 [2,2,2];

  • 第4个2:对比有效区域倒数第3个元素(nums[0]=2),相等,跳过;

  • 第1个3:对比 nums[0]=2,不相等,加入,k=4,有效区域 [2,2,2,3];

  • 第2个3:对比 nums[1]=2,不相等,加入,k=5,有效区域 [2,2,2,3,3];

  • 第3个3:对比 nums[2]=2,不相等,加入,k=6,有效区域 [2,2,2,3,3,3];

结果符合"最多保留3个重复元素"的要求,验证了通用规则的正确性。

四、复杂度分析

  • 时间复杂度:O(n),其中n为数组长度。仅需从左到右遍历一次数组,每个元素的判断和操作都是O(1)级别的,无嵌套循环;

  • 空间复杂度:O(1)。仅使用一个变量k标记有效区域,无其他额外辅助空间,完全原地操作,符合空间最优要求。

相关推荐
wuweijianlove1 小时前
算法性能的渐近与非渐近行为对比的技术4
算法
_dindong2 小时前
cf1091div2 C.Grid Covering(数论)
c++·算法
AI成长日志2 小时前
【Agentic RL】1.1 什么是Agentic RL:从传统RL到智能体学习
人工智能·学习·算法
黎阳之光2 小时前
黎阳之光:视频孪生领跑者,铸就中国数字科技全球竞争力
大数据·人工智能·算法·安全·数字孪生
skywalker_112 小时前
力扣hot100-3(最长连续序列),4(移动零)
数据结构·算法·leetcode
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 17. 电话号码的字母组合 | C++ 回溯算法经典模板
c++·算法·leetcode
wfbcg3 小时前
每日算法练习:LeetCode 209. 长度最小的子数组 ✅
算法·leetcode·职场和发展
_日拱一卒3 小时前
LeetCode:除了自身以外数组的乘积
数据结构·算法·leetcode
计算机安禾3 小时前
【数据结构与算法】第36篇:排序大总结:稳定性、时间复杂度与适用场景
c语言·数据结构·c++·算法·链表·线性回归·visual studio
SatVision炼金士3 小时前
合成孔径雷达干涉测量(InSAR)沉降监测算法体系
算法