我正在研究一个瓶颈为list1.Except(list2)的应用程序。从这篇文章:should i use Except or Contains when dealing with HashSet or so in Linq除了O(m + n)的复杂性(m和n代表列表的大小)。但是,我的列表已排序。这可以帮忙吗?
我能想到的第一个实现:
foreach element in list2 (m operations)
look for it in list1 (ln(n) operations)
if present
set it to null (O(1), removing has a O(n))
else continue
它具有复杂度O(m * ln(n)),当m很小而n很大时非常有趣(这与我的数据集完全相同:m大约为50,n大约是1 000 000 )。然而,它产生null的事实可能对使用它的函数有很多影响...有没有办法保持这种复杂性,而不必写空(然后跟踪它们)
非常感谢任何帮助!
答案 0 :(得分:2)
如果两个列表都已排序,那么您可以轻松实现自己的解决方案:
除listB算法之外的listA的工作方式如下:
1. Start from the beginning of both lists
2. If listA element is smaller than the listB element,
then include the listA element in the output and advance listA
3. If listB element is smaller than the listA element, advance listB
4. If listA and listB elements are equal,
advance both lists and do not push the element to the output
重复直到listA用尽。请特别注意listA可能会耗尽listB。
答案 1 :(得分:1)
using System;
using System.Collections.Generic;
public class Test
{
public static void Main()
{
var listM = new List<int>();
var listN = new List<int>();
for(int i = 0, x = 0; x < 50; i+=13, x++) {
listM.Add(i);
}
for(int i = 0, x = 0; x < 10000; i+=7, x++) {
listN.Add(i);
}
Console.WriteLine(SortedExcept(listM, listN).Count);
}
public static List<T> SortedExcept<T>(List<T> m, List<T> n) {
var result = new List<T>();
foreach(var itm in m) {
var index = n.BinarySearch(itm);
if(index < 0) {
result.Add(itm);
}
}
return result;
}
}
编辑以下是O(M + N)版本
public static List<T> SortedExcept2<T>(List<T> m, List<T> n) where T : IComparable<T> {
var result = new List<T>();
int i = 0, j = 0;
if(n.Count == 0) {
result.AddRange(m);
return result;
}
while(i < m.Count) {
if(m[i].CompareTo(n[j]) < 0) {
result.Add(m[i]);
i++;
} else if(m[i].CompareTo(n[j]) > 0) {
j++;
} else {
i++;
}
if(j >= n.Count) {
for(; i < m.Count; i++) {
result.Add(m[i]);
}
break;
}
}
return result;
}
快速&amp;脏基准http://ideone.com/Y2oEQD即使N是1000万,M + N总是更快。 BinarySearch受到惩罚,因为它以非线性方式访问数组内存;这会导致缓存未命中,从而减慢算法速度,因此较大的N会使用BinarySearch获得更多的内存访问权限。