如何高效提取两个std::unordered_map<std::string,std::set<A>>的增减元素?
更高效的
unordered_map<string, set<A>>差异提取实现 你的代码能实现基本的差异提取,但存在两个可优化的点:一是对set的差异计算用了多次find操作,时间效率有提升空间;二是未处理t1中存在但t2中不存在的key——这些key对应的整个集合都属于被删除的内容。
下面是优化后的实现,核心利用set的有序性,通过双指针遍历替代多次find,把单组set的差异计算时间复杂度从O(n log n + m log m)降到O(n + m):
#include <iostream> #include <string> #include <unordered_map> #include <set> #include <algorithm> struct A { std::string name; std::string address; auto operator<=>(const A& other) const = default; }; using T = std::unordered_map<std::string, std::set<A>>; void extractChanges(const T& t1, const T& t2, T& added, T& removed) { // 处理t2中的key:对比t1中同key的set差异 for (const auto& [key, set_t2] : t2) { auto it_t1 = t1.find(key); if (it_t1 == t1.end()) { // t1中无此key,整个set_t2都是新增 added.emplace(key, set_t2); continue; } const std::set<A>& set_t1 = it_t1->second; std::set<A> added_set, removed_set; // 双指针遍历两个有序set,找出差异 auto it1 = set_t1.begin(); auto it2 = set_t2.begin(); while (it1 != set_t1.end() && it2 != set_t2.end()) { if (*it1 < *it2) { // 元素在t1不在t2,属于删除 removed_set.insert(*it1); ++it1; } else if (*it2 < *it1) { // 元素在t2不在t1,属于新增 added_set.insert(*it2); ++it2; } else { // 元素相同,跳过 ++it1; ++it2; } } // 处理t1中剩余的元素(都是删除的) while (it1 != set_t1.end()) { removed_set.insert(*it1); ++it1; } // 处理t2中剩余的元素(都是新增的) while (it2 != set_t2.end()) { added_set.insert(*it2); ++it2; } if (!added_set.empty()) { added.emplace(key, std::move(added_set)); } if (!removed_set.empty()) { removed.emplace(key, std::move(removed_set)); } } // 处理t1中独有的key:整个set都是删除的 for (const auto& [key, set_t1] : t1) { if (t2.find(key) == t2.end()) { removed.emplace(key, set_t1); } } } int main() { T t1 = { {"key1", {{"name1", "address1"}, {"name2", "address2"}}}, {"key2", {{"name3", "address3"}}} }; T t2 = { {"key1", {{"name1", "address1"}, {"name2", "address2_modified"}}}, {"key3", {{"name4", "address4"}}} }; T added, removed; extractChanges(t1, t2, added, removed); // 打印验证结果 std::cout << "Added elements:\n"; for (const auto& [k, s] : added) { std::cout << "Key: " << k << "\n"; for (const auto& a : s) { std::cout << " " << a.name << " - " << a.address << "\n"; } } std::cout << "\nRemoved elements:\n"; for (const auto& [k, s] : removed) { std::cout << "Key: " << k << "\n"; for (const auto& a : s) { std::cout << " " << a.name << " - " << a.address << "\n"; } } return 0; }
关键优化细节
- 双指针遍历有序集合:因为
set是基于红黑树的有序容器,双指针可以线性遍历两个集合,一次遍历就能区分出新增、删除和相同元素,避免了原代码中每个元素O(log n)的find开销。 - 补全t1独有key的处理:原代码只处理了t2中存在的key,优化后的代码会遍历t1的所有key,把t2中没有的key对应的整个集合加入
removed,逻辑更完整。 - 移动语义减少拷贝:对临时构建的
added_set和removed_set使用std::move转移所有权,避免不必要的集合拷贝操作。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

