目前我的list
为1百万integers
,我会根据2000 integer
的黑名单检查每个integer
。这大约需要2分钟。
for(int i = 0; i< MillionIntegerList.Length ; i++)
{
for(int blacklisted = 0; blacklisted < TwoThousandIntegerList.Length ; blacklisted++)
if(i==blacklisted)
i = 0; //Zero is a sentinel value
}
这样就可以完成2,000,000,000次迭代(循环)。 有没有更好的方式我没有看到?感谢
答案 0 :(得分:51)
现在有三个选项 - 前两个更通用,因为它们不依赖MillionIntegerList
被排序(最初没有指定)。在大型列表 已经排序的情况下,第三种方法更可取。
选项1
是的,使用LINQ确实有更好的方法:
var common = MillionIntegerList.Intersect(TwoThousandIntegerList).ToList();
这将在内部使用通过HashSet<int>
构建的TwoThousandIntegerList
,然后查找其中MillionIntegerList
的每个元素 - 这将比通过整个{{TwoThousandIntegerList
更有效率每次1}}。
如果您只想要非黑名单的,则需要:
var valid = MillionIntegerList.Except(TwoThousandIntegerList).ToList();
请注意,如果您只需要迭代结果一次,则应删除ToList
调用 - 我已将其包含在内以实现结果,以便可以便宜地多次检查它们。如果您只是进行迭代,Intersect
或Except
的返回值只会流式结果,从而使其在内存使用方面更便宜。
选项2
如果您不想依赖LINQ to Objects的实现细节,但仍需要基于散列的方法:
var hashSet = new HashSet<int>(TwoThousandIntegerList);
hashSet.IntersectWith(MillionIntegerList);
// Now use hashSet
选项3
使用大型列表排序这一事实的方法肯定是有用的。
假设您不介意首先排序列入黑名单的列表,您可以编写像这样的流式(和通用)实现(未经测试):
// Note: to use this, you'd need to make sure that *both* sequences are sorted.
// You could either sort TwoThousandIntegerList in place, or use LINQ's OrderBy
// method.
public IEnumerable<T> SortedIntersect<T>(this IEnumerable<T> first,
IEnumerable<T> second) where T : IComparable<T>
{
using (var firstIterator = first.GetEnumerator())
{
if (!firstIterator.MoveNext())
{
yield break;
}
using (var secondIterator = second.GetEnumerator())
{
if (!secondIterator.MoveNext())
{
yield break;
}
T firstValue = firstIterator.Current;
T secondValue = secondIterator.Current;
while (true)
{
int comparison = firstValue.CompareTo(secondValue);
if (comparison == 0) // firstValue == secondValue
{
yield return firstValue;
}
else if (comparison < 0) // firstValue < secondValue
{
if (!firstIterator.MoveNext())
{
yield break;
}
firstValue = firstIterator.Current;
}
else // firstValue > secondValue
{
if (!secondIterator.MoveNext())
{
yield break;
}
secondValue = secondIterator.Current;
}
}
}
}
}
(如果您愿意,可以选择IComparer<T>
,而不是依赖T进行比较。)
答案 1 :(得分:17)
由于大型列表已排序。您可以通过排序小列表(非常快)然后进行线性合并来获得最佳结果。您只需要查看大(和小)列表中的每个项目一次,并且不需要在后台创建Hashtable。
请参阅MergeSort的merge function部分,了解如何执行此操作。
答案 2 :(得分:5)
我认为你需要的是Enumerable.Except方法(IEnumerable,IEnumerable)
答案 3 :(得分:3)
您的方法需要O(n * n)时间。考虑这些优化:
1)
如果你的整数不是太大,你可以使用bool数组(例如,如果最大可能的整数是1000000,则使用bool [] b = new bool [1000000])。现在要将数字K添加到黑名单,请使用b [K] = true。检查是微不足道的。这适用于O(n)。您也可以使用BitArray
2)
整数可以很大。使用二叉搜索树存储黑名单(例如SortedSet)。它有O(logN)插入和检索时间。所以它总是O(N * logN)。语法与List(Add(int K),Contains(int K))的语法相同,忽略重复
答案 4 :(得分:1)
我认为最好的解决方案是使用Bloom filter,而Bloom过滤器表示元素可能在黑名单中,只检查是否为误报(可以在O中完成(Log(可以) n))如果黑名单已分类)。 这个解决方案是节省时间的,并且几乎不使用额外的空间,这使得它比使用hashset要好得多。
这是Google用于Chrome中黑名单的解决方案。
答案 5 :(得分:1)
如何在较长的列表上进行二进制搜索,因为它已经排序。
foreach(integer blacklisted in TwoThousandIntegerList)
{
integer i = MillionIntegerList.binarySearch(blacklisted)
if(i==blacklisted){
//Do your stuff
}
}
此解决方案仅花费 O(m log n)时间,其中m是小列表的大小,n是较长列表的大小。 警告:此解决方案假定MillionIntegerList
没有重复值。
如果不是这种情况,那么你可以迭代重复,因为它们必须位于一个连续的块中。为此,我将假设MillionInterList
是一个记录列表,每个记录都包含value
和index
。
foreach(integer blacklisted in TwoThousandIntegerList)
{
integer index = MillionIntegerList.binarySearch(blacklisted)
//Find the index of the first occurrence of blacklisted value
while(index > 0 && MillionIntegerList[index - 1].value == blacklisted){
--index;
}
while(MillionIntegerList[index].value == blacklisted){
//Do your stuff
++index;
}
}
此解决方案的费用为 O(m log n + mk),其中 k 是MillionInterList
中每个黑名单整数的平均重复数。
答案 6 :(得分:0)
将HashSet用于阻止列表。
foreach(integer i in MillionIntegerList)
{
//check if blockedlist contains i
//do what ever you like.
}
答案 7 :(得分:-2)
对List使用Except
方法。这将有效