C / C ++ - 比较两个列表并找到缺失元素的有效方法

时间:2015-07-28 03:32:04

标签: c++ list sorting compare

我有两个列表,L 1 和L 2 ,包含抽象数据类型的多个元素(每个唯一元素)(即:structs )。两个列表中的每一个:

  • 可能包含零到一百(包含)元素。
  • 不包含重复元素(每个元素都是唯一的)。
  • 可能包含或不包含其他列表中的元素(即:L 1 且L 2 可能相同,或包含完全不同的元素)。
  • 未分类。
  • 在最低级别,存储有std::vector<myStruct>容器。

我通常期望的是,周期性地,将新元素添加到L 2 ,或者从中减去/删除元素。我试图尽可能有效地检测两个列表中的差异(即:使用最少的比较):

  • 如果L 2 中没有条目且L 1 中有条目,则执行一项操作:Handle_Missing_Element()
  • 如果L 2 中存在条目且L 1 中不存在,则执行另一项操作:Handle_New_Element()

执行上述检查后,L 1 设定为等于L 2 ,并且在将来的某个时间,L 2 < / sub>再次被检查。

我怎样才能找出两个清单之间的差异?我能想到两种方法:

  1. 通过每种可能的元素组合比较两个列表。可能是O(n 2 )执行复杂性(可怕)。
  2. 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()自动插入元素以使插入预先对列表进行排序,那么这样做是合理的。
    2. 有没有一种直接的方法可以在C ++中有效地实现这一目标?我发现了类似的问题,但是I need to do more than just find the intersection of two sets,或只用一组整数进行这样的测试,其中可以使用与sum相关的技巧,因为我需要对“new”vs“执行不同的操作”缺少“元素。

      谢谢。

4 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:3)

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

答案 2 :(得分:2)

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

x 元素插入 L2 时,您必须检查它是否与 L1 的所有元素相等。在每次与 y 相等时,增加 x y 元素的计数器。

当从 L2 中删除元素 x 时,您必须再次将其与 L1 的所有元素进行比较。在与 y y 的每次相等时,减去 y 的计数器。 x 的计数器无关紧要,因为它已被删除。

如果要查找非重复元素,可以简单地遍历两个数组。具有零计数器的元素是您需要的元素。

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

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

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

答案 3 :(得分:2)

  

之后手动对底层矢量进行排序是不切实际的   每个添加/删除列表。这样做才合理   如果以某种方式可以迫使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++ );