如何在std::unique处理vector后更新元素的新索引?
问题描述
现有一个函数,参数impObj指向vector<MyClass>类型容器myVec中的某个元素索引。调用std::unique去除连续重复元素并删除冗余元素后,需要更新impObj,使其指向原MyClass实例(或原连续重复组中的对应实例)在处理后vector中的新索引。
现有初始函数代码:
void RemoveSequentialDuplicates(std::vector<MyClass>& myVec, int& impObj) { auto itrLast = std::unique(myVec.begin(), myVec.end(), [](const MyClass& first, const MyClass& second) -> bool { return first == second; }); myVec.erase(itrLast, myVec.end()); }
需求示例
- 输入1:
[A, A, B, B, D, A],impObj = 5
输出1:[A, B, D, A],impObj = 3 - 输入2:
[A, B, B, B, A, A, C],impObj = 2
输出2:[A, B, A, C],impObj = 1
注:不强制使用std::unique,需保持元素顺序不变。当前已有一种实现方案,但希望找到更优方式:
void RemoveSequentialDuplicates(std::vector<MyClass>& myVec, int& index) { int leftIndex = 0, rightIndex = 1; auto itrLast = std::unique(myVec.begin(), myVec.end(), [&leftIndex, &rightIndex, &impObj](const MyClass& first, const MyClass& second) -> bool { bool equals = false; if (first == second) equals = true; else leftIndex++; if(impObj == rightIndex) impObj = leftIndex; rightIndex++; return equals; }); myVec.erase(itrLast, myVec.end()); }
更优实现方案
原方案的核心问题是在std::unique的lambda中维护多个状态变量,逻辑耦合度高,边界场景容易出错。以下两种方案将索引更新与去重逻辑解耦,可读性和稳定性更优:
方案一:预统计偏移量后更新索引
先计算原索引位置前被移除的连续重复元素数量,再基于这个偏移量更新目标索引:
void RemoveSequentialDuplicates(std::vector<MyClass>& myVec, int& impObj) { if (myVec.empty()) { impObj = -1; return; } // 统计原impObj位置前,会被std::unique移除的元素数量 int removedCount = 0; for (int i = 1; i <= impObj; ++i) { if (myVec[i] == myVec[i-1]) { removedCount++; } } // 执行去重操作 auto last = std::unique(myVec.begin(), myVec.end()); myVec.erase(last, myVec.end()); // 更新索引:原索引减去被删除的元素数量 impObj -= removedCount; }
该方案逻辑清晰,将索引计算与去重操作分离,时间复杂度保持O(n),且避免了lambda内的状态维护。
方案二:手动遍历构建新容器并跟踪索引
完全手动控制去重过程,同时同步更新目标索引,逻辑直观且边界场景处理更周全:
void RemoveSequentialDuplicates(std::vector<MyClass>& myVec, int& impObj) { if (myVec.empty()) { impObj = -1; return; } std::vector<MyClass> newVec; newVec.reserve(myVec.size()); // 预分配内存,减少扩容开销 newVec.push_back(myVec[0]); int newIndex = 0; bool indexUpdated = false; for (size_t i = 1; i < myVec.size(); ++i) { if (myVec[i] != newVec.back()) { newVec.push_back(myVec[i]); newIndex++; } // 遍历到原目标索引时,记录对应的新索引 if (!indexUpdated && static_cast<int>(i) == impObj) { impObj = newIndex; indexUpdated = true; } } // 处理原索引为0的特殊情况 if (!indexUpdated && impObj == 0) { impObj = 0; } myVec.swap(newVec); // 交换容器,避免大规模拷贝 }
该方案无需依赖std::unique,手动处理逻辑更透明,swap操作的性能开销极低,同时能覆盖所有边界场景(如容器为空、目标索引为第一个元素等)。
内容的提问来源于stack exchange,提问作者Mukund
相关产品推荐
相关产品推荐

