C#: хорошая / лучшая реализация метода Swap

Я прочитал это пост про карты тасуем и во многих алгоритмах перетасовки и сортировки вам нужно поменять местами два элемента в списке или массиве. Но как выглядит хороший и эффективный метод Swap?

скажем, для T[] и List<T>. Как лучше всего реализовать метод, который меняет местами два элемента в этих двух?

Swap(ref cards[i], ref cards[n]);   // How is Swap implemented?

5 ответов


Ну, код, который вы выложили (ref cards[n]) может работать только с массивом (а не со списком) - но вы бы использовали просто (где foo и bar два значения):

static void Swap(ref int foo, ref int bar) {
    int tmp = foo;
    foo = bar;
    bar = tmp;
}

или, возможно (если вы хотите atomic):

Interlocked.Exchange(ref foo, ref bar);

лично я не думаю, что буду беспокоиться о методе swap, хотя-просто сделайте это напрямую; это означает ,что вы можете использовать (либо для списка, либо для массива):

int tmp = cards[n];
cards[n] = cards[i];
cards[i] = tmp;

если вы действительно хотели напишите метод swap, который работал либо на list или массив, вам нужно будет сделать что-то вроде:

static void Swap(IList<int> list, int indexA, int indexB)
{
    int tmp = list[indexA];
    list[indexA] = list[indexB];
    list[indexB] = tmp;
}

(было бы тривиально сделать это общим) - однако оригинальная "встроенная" версия (т. е. не метод), работающая над массивом, будет быстрее.


использование:

void swap(int &a, int &b)
{
    // &a != &b
    // a == b OK
    a ^= b;
    b ^= a;
    a ^= b;
    return;
}

Я не понял, что я был в разделе C#. Это код C++, но он должен иметь ту же основную идею. Я считаю, что ^ - это XOR в C#. Похоже, вместо & вам может понадобиться "ref" (?). Я не уверен.


хороший своп-это тот, где вы не меняете содержимое. В C/C++ это было бы сродни замене указателей вместо замены содержимого. Этот стиль замены является быстрым и поставляется с некоторой гарантией исключения. К сожалению, мой C# слишком ржавый, чтобы позволить мне поместить его в код. Для простых типов данных, этот стиль не дает вам много. Но как только вы привыкнете и будете иметь дело с более крупными (и более сложными) объектами, это может спасти вашу жизнь.


Как насчет этого? Это общая реализация метода swap. 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 Лоренцо!--2-->


для тех, кому интересно, замена также может быть выполнена также с помощью методов расширения (.NET 3.0 и новее).

В общем, кажется, нет возможности сказать, что методы расширения "это" значение 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);