【问题标题】:C/C++ - Efficient way to compare two lists and find missing elementsC/C++ - 比较两个列表和查找缺失元素的有效方法
【发布时间】:2015-10-18 10:54:24
【问题描述】:

我有两个列表,L1 和 L2,包含多个元素的数据,每个元素都是抽象数据类型的(即:structs)。两个列表中的每一个:

  • 可能包含零到一百个(含)元素。
  • 不包含重复元素(每个元素都是唯一的)。
  • 可能包含也可能不包含其他列表中的元素(即:L1 和 L2 可能相同,或包含完全不同的元素)。
  • 未排序。
  • 在最低级别,存储在 std::vector<myStruct> 容器中。

我通常期望的是定期向 L2 添加一个新元素,或者从中减去/删除一个元素。我正在尝试尽可能有效地检测两个列表中的差异(即:用最少的比较):

  • 如果条目不存在于 L2 中而存在于 L1 中,则执行一项操作:Handle_Missing_Element()
  • 如果条目存在于 L2 中且不存在于 L1 中,则执行另一个操作:Handle_New_Element()

一旦执行了上述检查,L1 被设置为等于 L2,并且在将来的某个时间,L2 再次检查。

我怎样才能找出这两个列表之间的差异?我可以想到两种方法:

  1. 通过每个可能的元素组合比较两个列表。可能 O(n2) 执行复杂度(可怕)。

bool found;
for i in 1 .. L2->length()
  found = false;
  for j in 1 .. L1->length()
    if (L1[j] == L2[i]
      // Found duplicate entry
      found = true;
    fi
  endfor
endfor
  1. 对列表进行排序,并逐个元素比较这两个列表,直到找到不同之处。这似乎是在接近线性的时间内。问题是我需要对列表进行排序。在列表的每次添加/删除之后手动对基础向量进行排序是不切实际的。只有在某种程度上可以强制vector::push_back() 自动插入元素以便插入保留列表的排序时,这样做才是合理的。

有没有一种直接的方法可以在 C++ 中有效地完成此任务?我发现了类似的此类问题,但是I need to do more than just find the intersection of two sets,或者只使用一组整数进行这样的测试,其中可以使用与总和相关的技巧,因为我需要对“新”与“缺失”执行不同的操作元素。

谢谢。

【问题讨论】:

  • C语言中std::vector<myStruct>不好用,建议去掉C标签。
  • 那么,您的列表并不是真正的链表(如std::list),而是数组(如std::vector)?
  • 你有元素比较功能吗? (我的意思是operator<,而不仅仅是operator==。)
  • @stgatilov 正确,L1 是常数。
  • @Beta 我没有比较功能。此时它只是一个struct,而不是一个完全定义的class

标签: c++ list sorting compare


【解决方案1】:

在之后手动对底层向量进行排序是不切实际的 列表的每次添加/删除。这样做是合理的 如果有可能以某种方式强制vector::push_back() 自动插入元素,以便插入保留排序 列表。

你在这里谈论的是一个有序插入<algorithm> 中有一些函数可以让你这样做。与其使用std::vector::push_back,不如使用std::vector::insert,并调用std::lower_bound,它对不小于给定值的第一个元素进行二分搜索。

auto insert_pos = std::lower_bound( L2.begin(), L2.end(), value );
if( insert_pos == L2.end() || *insert_pos != value )
{
    L2.insert( insert_pos, value );
}

这使得每次插入 O(logN) 但如果您在定期检查之间进行的插入少于 N 次,则应该是一种改进。

压缩操作可能如下所示:

auto it1 = L1.begin();
auto it2 = L2.begin();

while( it1 != L1.end() && it2 != L2.end() )
{
    if( *it1 < *it2 ) {
        Handle_Missing( *it1++ );
    } else if( *it2 < *it1 ) {
        Handle_New( *it2++ );
    } else {
        it1++;
        it2++;
    }
}

while( it1 != L1.end() ) Handle_Missing( *it1++ );
while( it2 != L2.end() ) Handle_New( *it2++ );

【讨论】:

  • 在向量中间插入需要 O(N) 时间。
  • 在实践中,向量插入比列表更快,对于任何包含相当淫秽大小的类型。我认为如果 OP 确定了他们维护这两个列表的为什么,那将会有所帮助。我会建议在队列中提供操作,然后将它们嘎嘎作响。或者将所有内容存储在树中。
  • @paddy 我正在跟踪音频/DSP 系统中新连接/断开的麦克风,并且需要告诉底层软件为新麦克风分配缓冲区,或者清理并释放麦克风的缓冲区不再连接到系统。我唯一能识别麦克风的唯一方法是通过硬件内置的硬编码 UUID。目前,我没有断开/连接事件处理能力,必须依靠轮询所有连接的音频设备(潜在的麦克风)。
  • @dogbert 听起来,您可能只需将 L1 保持为排序向量(使用有序插入),并完全摆脱 L2。当您枚举连接的设备 UUID 时,您可以在 L1 中对每个 UUID 进行二进制搜索(使用 std::binary_search),然后将其推送到“添加”或“删除”向量中。枚举后,遍历这些向量,调用适当的处理程序并更新L1
  • @paddy:如果它是您建议的排序向量,那么使用 std::set 会不会更自然,因为列表不包含重复元素。 std::set 在内部按比较对象排序。
【解决方案2】:

你能为你的列表项创建一个哈希值吗?如果是这样,只需计算哈希并检查其他列表的哈希表。这很快,不需要排序,并且可以防止您的“所有可能的组合”问题。如果您使用 C++ 和 STL,您可以使用 map 容器来保存每个列表。

  • 为 L1 中的每个项目创建一个哈希,并使用 map 将其映射到您的列表项。
  • 为 L2 创建一个类似的地图,并在创建每个 L2 时检查它是否在 L1 地图中。
  • 当一个新元素添加到 L2 时,计算它的哈希值并检查它是否在 L1 哈希映射中(如果使用 STL 映射,则使用map.find())。如果没有,则执行您的Handle_New_Element() 功能。
  • 当从 L2 列表中减去一个元素并且它的哈希不在 L1 哈希映射中时,请执行您的 Handle_Missing_Element() 函数。

【讨论】:

  • 确定列表是否不同的好主意。但似乎 OP 还需要查找缺少哪些元素。
  • 谢谢。这确实让我能够检测到两个列表之间的差异,但我需要能够找到缺失的元素和新元素,并区分两者。
  • 其实我认为你可以检测到缺失的元素...等待,我会更新我的答案。
  • 在哈希冲突的情况下,您的解决方案似乎不正确。不过,如果散列很大,这在实践中可能不是很重要。
  • 让哈希表X 和哈希表Y 存储任意类型的大小表示。让排序序列Z 表示哈希表XY 之间的差异。也就是说,当您插入Y 时,还要检查X,如果它们不同,请将差异存储在Z
【解决方案3】:

在插入时自动排序的容器是std::set。插入将是 O(log n),比较两组将是 O(n)。由于您的所有元素都是独一无二的,因此您不需要std::multiset

【讨论】:

    【解决方案4】:

    对于两个数组的每个元素,保持在相反数组中遇到的次数。您可以将这些数字存储在具有相同索引的单独数组中,或者存储在您使用的结构中。

    当一个元素 x 被插入到 L2 中时,你必须检查它是否与 L1 的所有元素相等。在每个与 y 相等时,增加两个元素 xy 的计数器。

    当一个元素 xL2 中移除时,你必须再次将它与 L1 的所有元素进行比较。在每个与 L1 中的 y 相等时,减少 y 的计数器。 x 的计数器无关紧要,因为它已被删除。

    当您想查找不重复的元素时,您可以简单地遍历两个数组。计数器为零的元素就是您需要的元素。

    总的来说,每次插入和删除需要 O(|L1|) 次额外操作,每次重复搜索需要 O(|L1| + |L2|) 次操作.如果您另外维护具有零计数器的所有元素的列表,则后者可以减少到寻找的非重复元素的数量。

    编辑:糟糕,由于每个列表的唯一性,每个计数器似乎总是 0 或 1。

    EDIT2:正如Thane Plummer 所写,您还可以使用哈希表。如果您为L1 创建一个哈希表,那么您可以在O(1) 中进行插入和删除中的所有比较。顺便说一句,因为你的 L1 是不变的,你甚至可以创建 a perfect hash table 让它更快。

    【讨论】:

      猜你喜欢
      • 2019-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-19
      • 2022-09-27
      相关资源
      最近更新 更多