我读过这篇关于洗牌的文章,在许多洗牌和排序算法中,您需要交换列表或数组中的两个项目。但是一个好的高效的 Swap 方法是什么样的呢?
让我们说 aT[]
和 a List<T>
。您将如何最好地实现一种在这两个项目中交换两个项目的方法?
Swap(ref cards[i], ref cards[n]); // How is Swap implemented?
好吧,您发布的代码 ( ref cards[n]
) 只能与数组(而不是列表)一起使用 - 但您可以简单地使用(其中foo
和bar
是两个值):
static void Swap(ref int foo, ref int bar) {
int tmp = foo;
foo = bar;
bar = tmp;
}
或者可能(如果你想要原子):
Interlocked.Exchange(ref foo, ref bar);
不过,就我个人而言,我认为我不会打扰交换方法 - 直接进行即可;这意味着您可以使用(对于列表或数组):
int tmp = cards[n];
cards[n] = cards[i];
cards[i] = tmp;
如果您真的想编写一个适用于列表或数组的交换方法,则必须执行以下操作:
static void Swap(IList<int> list, int indexA, int indexB)
{
int tmp = list[indexA];
list[indexA] = list[indexB];
list[indexB] = tmp;
}
(使这个通用化是微不足道的) - 但是,在数组上工作的原始“内联”版本(即不是方法)会更快。
一个好的交换是你不交换内容的地方。在 C/C++ 中,这类似于交换指针而不是交换内容。这种交换方式很快并且带有一些异常保证。不幸的是,我的 C# 太生锈了,无法将其放入代码中。对于简单的数据类型,这种风格并没有给你太多。但是,一旦您习惯并且必须处理更大(更复杂)的对象,它就可以挽救您的生命。
那这个呢?它是交换方法的通用实现。Jit 将为您的封闭类型创建一个编译版本,因此您不必担心性能!
/// <summary>
/// Swap two elements
/// Generic implementation by LMF
/// </summary>
public static void Swap<T>(ref T itemLeft, ref T itemRight) {
T dummyItem = itemRight;
itemLeft = itemRight;
itemRight = dummyItem;
}
HTH 洛伦佐
11 年后,我们有了元组......
(foo, bar) = (bar, foo);
采用:
void swap(int &a, int &b)
{
// &a != &b
// a == b OK
a ^= b;
b ^= a;
a ^= b;
return;
}
我没有意识到我在 C# 部分。这是 C++ 代码,但它应该具有相同的基本思想。我相信 ^ 在 C# 中也是 XOR。看起来&
你可能需要“ref”(?)。我不确定。
您现在可以使用元组来完成此交换,而无需手动声明临时变量:
static void Swap<T>(ref T foo, ref T bar)
{
(foo, bar) = (bar, foo)
}
对于任何想知道的人,也可以使用扩展方法(.NET 3.0 和更高版本)进行交换。
一般来说,似乎不可能说扩展方法“this”的值是 ref,因此您需要返回它并覆盖旧值。
public static class GeneralExtensions {
public static T SwapWith<T>(this T current, ref T other) {
T tmpOther = other;
other = current;
return tmpOther;
}
}
然后可以像这样使用此扩展方法:
int val1 = 10;
int val2 = 20;
val1 = val1.SwapWith(ref val2);