原地去重且保持顺序用unordered_set配双指针最稳妥;若允许重排,sort+unique效率最高但破坏顺序;unique必须先排序,返回新逻辑结尾迭代器,需手动计算长度。

原地去重且保持顺序,用 std::unordered_set 配合双指针最稳妥;若允许重排,std::sort + std::unique 组合效率最高,但会破坏原始顺序。
std::sort + std::unique 必须先排序
std::unique 只能消除相邻重复元素,不排序直接调用等于白做。比如 int arr[] = {3,1,3,2,1},调用 std::unique(arr, arr+5) 后仍是 {3,1,3,2,1},因为重复的 3 和 1 不相邻。
- 必须先调用
std::sort(arr, arr+n),让重复值聚到一起 -
std::unique返回的是新逻辑结尾的迭代器,不是数组长度,要手动计算:int new_len = std::unique(arr, arr+n) - arr - 它不真正删内存,只是把重复元素挪到末尾,后续需配合
erase(对vector)或靠new_len截断访问 - 时间复杂度
O(n log n),空间O(1)(原数组)
std::unordered_set 去重并保持首次出现顺序
如果输入是 vector<int></int>,且你希望保留每个数字第一次出现的位置(比如去重后仍为 [3,1,2] 而非排序后的 [1,2,3]),std::unordered_set 是首选。
- 插入时自动判重,
insert()返回pair<iterator bool></iterator>,其中second为true表示新插入 - 遍历时不能依赖
unordered_set的遍历顺序——它无序;应边插入边记录有效元素到新vector - 常见错误:只存
set然后直接遍历输出,结果顺序随机,和原数组无关 - 示例关键逻辑:
if (seen.insert(x).second) unique_vec.push_back(x);
双指针原地去重(仅适用于已排序数组)
对升序数组(如 [0,0,1,1,1,2,2,3]),双指针法能做到 O(n) 时间、O(1) 空间、真正原地修改,但前提是“已排序”这个条件必须满足。
立即学习“C++免费学习笔记(深入)”;
- 慢指针
slow指向去重后数组的最后一个位置,初始为0 - 快指针
fast从1开始扫描,每次比较nums[fast] != nums[slow],成立则slow++并赋值 - 返回值是
slow + 1,即新长度;原数组前slow+1项即为结果 - 若误用于未排序数组,会漏掉非相邻重复项,例如
[1,2,1]→ 输出[1,2,1](第三个1不被识别)
vector::erase 配合迭代器遍历的陷阱
用迭代器边遍历边 erase 看似直观,但极易出错:每次 erase 后迭代器失效,必须用其返回值重新赋值,否则可能越界或跳过元素。
- 正确写法:
it = nums.erase(it);——erase返回下一个有效迭代器 - 错误写法:
nums.erase(it); ++it;——erase后it已无效,再++是未定义行为 - 该方法时间复杂度达
O(n²)(每次erase移动后续元素),大数据量时明显卡顿 - 仅适合小规模、对性能不敏感的场景,或作为教学演示
真正要注意的不是“哪种方法最快”,而是“是否允许改变顺序”和“输入是否已排序”——这两个前提没确认清楚,再短的代码也得不到正确结果。


















