2009-02-16 7 views
10

Я читал это post about card shuffling и во многих алгоритмах перетасовки и сортировки вам нужно поменять два элемента в списке или массиве. Но как выглядит хороший и эффективный метод Swap?C#: Хорошая/лучшая реализация метода свопинга

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

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

ответ

22

Ну, код, который вы выложили (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; 
} 

(это было бы тривиально, чтобы сделать это родовое) - однако, оригинальная версия «инлайн» (т.е. не метод) работает на массив будет быстрее.

+0

Interlocked.Exchange? – Svish

+0

Как это работает с массивом? – Svish

+0

Забастовка обмена ... –

3

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

+3

Но для int [] или List , содержимое является одинаковым (x86) или половинным размером (x64). В этом случае замените содержимое. –

5

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

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

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

+0

Интересно ... что означают эти комментарии? – Svish

+5

(И это 'return;' действительно нужно?) – Svish

+0

return не нужен, и вы должны использовать ref вместо & yes, +1 –

3

Что относительно этого? Это общая реализация метода свопинга. 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; 
} 

НТН Лоренцо

0

Для тех, кто интересно, подкачка также может быть сделано также с помощью методов, расширение (.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);