从2个集合中查找添加和删除的高效算法

时间:2010-08-26 16:55:08

标签: c# algorithm set

您好我想实现一个有效的算法来处理以下情况:

假设我们有2个列表,其中包含以下元素:

来源:[a,b,c,d,e] 新:[d,e,f,g]

现在我必须使用新信息更新源代码。算法应该能够找到'f'和'g'是新条目,'a','b'和'c'已被删除,'d'和'e'没有被修改。

所涉及的操作是Source和New之间的set-intersect操作,反之亦然。我正在寻找一种有效的算法来在C#中实现任意非排序的枚举。

提前致谢,

5 个答案:

答案 0 :(得分:6)

var added = New.Except(Source);
var removed = Source.Except(New);
var notModified = Source.Intersect(New);

如果你想要一个“展示你的工作”的方法,我建议你把它们分别放到HashSet中,因为与其他枚举相比,它允许快速Contains检查。

编辑:

好的,如果我们以牺牲表达效率为代价来提高总速度,那么请遵循以下假设:

  1. 我们有一个合理的可散列类型的项目(如果没有,但它们可以绝对排序,那么SortedList可能会击败散列集)。
  2. 我们无法预测Source或New是否会更大(在这个例子中,这样做有一点点优势,反过来说我有这个,但我假设这只是偶然的数据和那个我们必须期望每个人都有相同的可能性。
  3. 然后我会建议:

    HashSet<T> removed = Source as HashSet<T> ?? new HashSet<T>(Source);
    LinkedList<T> added = new LinkedList<T>();
    LinkedList<T> notModified = new LinkedList<T>();
    foreach(T item in New)
        if(removed.Remove(item))
            notModified.AddLast(item);
        else
            added.AddLast(item);
    

    在设置removed时,我测试它是否已经是一个散列集以避免浪费地构建一个新的(我假设输入被输入为IEnumerable<T>)。当然,这是一种破坏性的行为,所以我们可能希望无论如何都要避免它。

    另请注意,我在枚举时修改了hashset。这是hashset允许的,但在枚举数给出的保证之外,依赖于实现。仍然,与目前的框架impl。这样做比测试和添加到不同的删除集合更有效。

    我选择了另外两个集合的链接列表,因为它们在插入成本方面往往很好(不仅仅是O(1),而是使用另一个集合时快速O(1)。 / p>

    现在,如果你想更进一步,如果你自己动手,可能会在哈希集的实现中提供微优化。

答案 1 :(得分:3)

我没有对性能进行测试,但我的直觉是你应该首先对两个列表进行排序。然后,您可以在逐步执行列表键时逐步删除,添加或更改元素。

1- Sort the Old and New list
2- Set up a pointer for each list lets call them p1 and p2
3- Step the pointers using the following algorithm
  a) If Old[p1] = New[p2] the items are unchanged, increment p1 and p2
  b) If Old[p1] < New[p2] then Old[p1] has been removed, increment p1
  c) If Old[p1] > new[p2] then New[p2] is a new element, increment p2
  d) If p1 > Old.ItemCount then break out of loop, rest of New contains new items
  e) If p2 > New.ItemCount then break out of loop, rest of Old items have been removed
  f) If p1 < Old.ItemCount and p2 < Old.ItemCount Goto step **a**

这只是我的头脑,但基本应该是正确的。关键是这些列表当然是排序的。

这是一个快速而肮脏的演示,我包含了用于演示目的的类型,当然在这种情况下数据已经排序。

static void Main(string[] args)
{
  string[] oldList = { "a", "b", "c", "d", "e" };
  string[] newList = { "d", "e", "f", "g" };      

  Array.Sort(oldList);
  Array.Sort(newList);

  int p1 = 0;
  int p2 = 0;

  while (p1 < oldList.Length && p2 < newList.Length)
  {
    if (string.Compare(oldList[p1], newList[p2]) == 0)
    {
      Console.WriteLine("Unchanged:\t{0}", oldList[p1]);
      p1++;
      p2++;
    }
    else if (string.Compare(oldList[p1], newList[p2]) < 0)
    {
      Console.WriteLine("Removed:\t{0}", oldList[p1]);
      p1++;
    }
    else if (string.Compare(oldList[p1], newList[p2]) > 0)
    {
      Console.WriteLine("Added:\t\t{0}", newList[p2]);
      p2++;
    }        
  }

  while (p1 < oldList.Length)
  {
    Console.WriteLine("Removed:\t{0}", oldList[p1]);
    p1++;
  }

  while (p2 < newList.Length)
  {
    Console.WriteLine("Added :\t\t{0}", newList[p2]);
    p2++;
  }

  Console.ReadKey();
}

以上

的输出
Removed:        a
Removed:        b
Removed:        c
Unchanged:      d
Unchanged:      e
Added :         f
Added :         g

答案 2 :(得分:1)

您可以使用Linq中提供的 set operations

string[] list1 = { "a","b","c","d","e"};
string[] list2 = { "d", "e", "f", "g" };

string[] newElements = list2.Except(list1).ToArray();
string[] commonElements = list2.Intersect(list1).ToArray();
string[] removedElements = list1.Except(list2).ToArray(); 

注意:上面的代码假定每个列表都是不同的,即多次不包含相同的元素。例如,对于列表[a,b,c,c]和[a,b,c],代码将不会检测已删除的元素。

答案 3 :(得分:1)

调用集合X和Y.如果集合X支持快速查找,并且您可以方便地“标记”和“取消标记”其中的项目,则可以首先标记X中的所有项目,然后查询X对于Y中的每个项目。如果找不到项目,则项目在Y中为“新”。如果找到该项目,则两个集合都是通用的,您应该在X中取消它。对Y中的所有项目重复。你完成了,X中任何仍然被标记的项目都已从Y中“删除”。

此方法仅需要其中一个集合来支持方便的查询和标记。它需要查询另一组中所有记录的一组,然后从中获取所有未生成命中的项目。没有要求对任何一组进行排序。

答案 4 :(得分:0)

我认为你所看到的是设定操作,即工会等。看一下这篇文章:http://srtsolutions.com/public/item/251070