合并2组排序值的最快方法是什么?速度(大O)在这里很重要;不清楚 - 假设这已经完成了数百万次。
假设您不知道值的类型或范围,但有一个高效的IComparer<T>
和/或IEqualityComparer<T>
。
给出以下数字:
var la = new int[] { 1, 2, 4, 5, 9 };
var ra = new int[] { 3, 4, 5, 6, 6, 7, 8 };
我期待1,2,3,4,5,6,7,8,9。以下存根可用于测试代码:
static void Main(string[] args)
{
var la = new int[] { 1, 2, 4, 5, 9 };
var ra = new int[] { 3, 4, 5, 6, 6, 7, 8 };
foreach (var item in UnionSorted(la, ra, Int32Comparer.Default))
{
Console.Write("{0}, ", item);
}
Console.ReadLine();
}
class Int32Comparer : IComparer<Int32>
{
public static readonly Int32Comparer Default = new Int32Comparer();
public int Compare(int x, int y)
{
if (x < y)
return -1;
else if (x > y)
return 1;
else
return 0;
}
}
static IEnumerable<T> UnionSorted<T>(IEnumerable<T> sortedLeft, IEnumerable<T> sortedRight, IComparer<T> comparer)
{
}
答案 0 :(得分:3)
以下方法返回正确的结果:
static IEnumerable<T> UnionSorted<T>(IEnumerable<T> sortedLeft, IEnumerable<T> sortedRight, IComparer<T> comparer)
{
var first = true;
var continueLeft = true;
var continueRight = true;
T left = default(T);
T right = default(T);
using (var el = sortedLeft.GetEnumerator())
using (var er = sortedRight.GetEnumerator())
{
// Loop until both enumeration are done.
while (continueLeft | continueRight)
{
// Only if both enumerations have values.
if (continueLeft & continueRight)
{
// Seed the enumeration.
if (first)
{
continueLeft = el.MoveNext();
if (continueLeft)
{
left = el.Current;
}
else
{
// left is empty, just dump the right enumerable
while (er.MoveNext())
yield return er.Current;
yield break;
}
continueRight = er.MoveNext();
if (continueRight)
{
right = er.Current;
}
else
{
// right is empty, just dump the left enumerable
if (continueLeft)
{
// there was a value when it was read earlier, let's return it before continuing
do
{
yield return el.Current;
}
while (el.MoveNext());
} // if continueLeft is false, then both enumerable are empty here.
yield break;
}
first = false;
}
// Compare them and decide which to return.
var comp = comparer.Compare(left, right);
if (comp < 0)
{
yield return left;
// We only advance left until they match.
continueLeft = el.MoveNext();
if (continueLeft)
left = el.Current;
}
else if (comp > 0)
{
yield return right;
continueRight = er.MoveNext();
if (continueRight)
right = er.Current;
}
else
{
// The both match, so advance both.
yield return left;
continueLeft = el.MoveNext();
if (continueLeft)
left = el.Current;
continueRight = er.MoveNext();
if (continueRight)
right = er.Current;
}
}
// One of the lists is done, don't advance it.
else if (continueLeft)
{
yield return left;
continueLeft = el.MoveNext();
if (continueLeft)
left = el.Current;
}
else if (continueRight)
{
yield return right;
continueRight = er.MoveNext();
if (continueRight)
right = er.Current;
}
}
}
}
空间为~O(6),时间为〜(max(n,m))(其中m为第二组)。
答案 1 :(得分:2)
我要给LINQ带来疑问,并说这可能和你没有写过多代码一样快:
var result = la.Union(ra);
EDITED: 谢谢,我错过了排序部分。
你可以这样做:
var result = la.Union(ra).OrderBy(i => i);
答案 2 :(得分:2)
这会使你的UnionSorted功能变得不那么通用,但你可以通过对类型做出假设来做一些改进。如果你在循环内部进行比较(而不是调用Int32Comparer)那么这将节省一些函数调用开销。
所以你的UnionSorted声明变成了这个......
static IEnumerable<int> UnionSorted(IEnumerable<int> sortedLeft, IEnumerable<int> sortedRight)
然后你在循环中执行此操作,摆脱对comparer.Compare()
...
//var comp = comparer.Compare(left, right); // too slow
int comp = 0;
if (left < right)
comp = -1;
else if (left > right)
comp = 1;
在我的测试中,这个速度提高了大约15%。
答案 3 :(得分:0)
我会这样解决问题。 (我正在做一个明显减轻这个问题难度的假设,只是为了说明这个想法。)
假设:集合中包含的所有数字均为非负数。
创建一个至少n
位的单词,其中n
是您期望的最大值。 (如果您期望的最大值为12,则必须创建一个16位的字。)。
迭代两组。对于每个值,val
,or
val
位1
。
完成后,计算设置为1
的位数。创建该大小的数组。
逐个浏览每个位,如果设置了n
,则将n
添加到新数组。