您好我想实现一个有效的算法来处理以下情况:
假设我们有2个列表,其中包含以下元素:
来源:[a,b,c,d,e] 新:[d,e,f,g]
现在我必须使用新信息更新源代码。算法应该能够找到'f'和'g'是新条目,'a','b'和'c'已被删除,'d'和'e'没有被修改。
所涉及的操作是Source和New之间的set-intersect操作,反之亦然。我正在寻找一种有效的算法来在C#中实现任意非排序的枚举。
提前致谢,
答案 0 :(得分:6)
var added = New.Except(Source);
var removed = Source.Except(New);
var notModified = Source.Intersect(New);
如果你想要一个“展示你的工作”的方法,我建议你把它们分别放到HashSet中,因为与其他枚举相比,它允许快速Contains
检查。
编辑:
好的,如果我们以牺牲表达效率为代价来提高总速度,那么请遵循以下假设:
然后我会建议:
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