2009-03-07 17 views
18

Jaki jest najskuteczniejszy sposób usunięcia elementów alternatywnych (nieparzystych, indeksowanych lub indeksowanych) w List<T> bez używania zmiennej listy elementów umieszczonych?Usuwanie alternatywnych elementów z listy <T>

Również byłoby mile widziane, gdyby można było wspomnieć o kosztach z każdą odpowiedzią.

Czekam na efektywnego sposób to zrobić

góry dziękuję

+0

Co dokładnie oznaczasz na przemian? –

+0

Czy chcesz usunąć wszystkie inne elementy, tj. Wszystkie indeksy parzyste lub wszystkie elementy nieparzystego indeksu? –

+0

tak, nawet indeksowane lub nieparzyste elementy indeksowane. – abhilash

Odpowiedz

25

Jeśli zadzwonisz RemoveAt dla każdego elementu usunąć, będzie przenoszenie dużej ilości danych. Najbardziej efektywny jest, aby przenieść elementy razem, które chcesz zachować, a następnie usunąć nieużywane elementy na koniec:

int pos = 0; 
for (int i = 0; i < values.Count; i += 2, pos++) { 
    values[pos] = values[i]; 
} 
values.RemoveRange(pos, values.Count - pos); 

Edit:
Metoda ta będzie przetwarzać listę milion wskazówki w 15 ms. Używanie RemoveAt zajmie ponad trzy minuty ...

Edycja2:
Moglibyśmy zacząć od pos = 1 i i = 2 (lub 3), ponieważ pierwszy element nie musi być kopiowany do siebie. To sprawia, że ​​kod jest nieco mniej oczywisty.

+1

Myślę, że to zadziała tylko wtedy, gdy liczba elementów na liście jest parzysta. – tvanfosson

+0

Myślę, że muszę tylko zacząć od 1 nie 0 to ten kod zadziała – AnthonyWJones

+0

@tvanfosson: Nie, sprawdziłem, że kod działa zarówno dla parzystej, jak i nieparzystej liczby elementów. @AnthonyWJones: Jeśli chcesz usunąć równe indeksy zamiast nieparzystych, zaczniesz od 1 zamiast 0. – Guffa

2

Nie jestem pewien co masz na myśli przez alternatywny ale jeśli masz na myśli „co drugi element” Poniższy kod będzie działać. rozpocznie usuwając 2nd elementu, wówczas 4th, i tak dalej

List<T> list = GetTheList(); 
int i = 1; 
while (i < list.Count) { 
    list.RemoveAt(i); 
    i++; 
} 
+0

Po usunięciu elementu na początku cała reszta zmieni się o jeden. To na pewno nie wyeliminuje elementów alternatywnych i może spowodować wyjątek w zależności od tego, czy strażnik jest ponownie obliczany na każdym kroku, tj. Lista. Wartość będzie się zmieniać. – tvanfosson

+0

@tvanfonsson: Kod wygląda dobrze dla mnie? – AnthonyWJones

+0

@Anthony - gdy usuniesz element w pozycji 2, element w pozycji 3 przesunie się na 2, 4 zmiany na 3 itd., Więc następny element, który zostanie usunięty, znajduje się na pozycji 5 na oryginalnej liście, a nie na pozycji 4. – tvanfosson

7

Tylko dla rozważenia rozwiązanie, które tworzy nową listę z listą stary można to zrobić:

var newList = old.Where((_, i) => i%2 != 0).ToList(); 

lub, oczywiście

var newList = l.Where((_, i) => i%2 == 0).ToList(); 

zależności, które naprzemiennie wybrać.

EDIT

Odpowiedź jest dość nieco szybciej. Jeśli czytasz coś innego tutaj, to dlatego, że mierzyłem w weekend, a mózg weekendu jest zabawny. :( Rozwiązanie zamknięcia jest o 40% szybsze, podczas gdy odpowiedź jest szybsza o 2 rzędy wielkości, przypuszczam, że to naprawdę zależy od tego, jak duża staje się twoja lista:

+0

Myślę, że wstawiłeś tam kod F # :) – JaredPar

+0

Nie jestem pewien, czy mówisz to ironicznie? Jest to zdecydowanie kod C#, próbowałem go, zanim opublikowałem go tutaj. Szczerze mówiąc, jestem nieco zaskoczony, że nie dostaję żadnego repa, biorąc pod uwagę, że to najprostsza odpowiedź ze wszystkich ... – flq

+0

To nie odpowiada na żadne z wymagań PO. Chce usunąć elementy z listy, której ten kod nie zawiera. Chce wydajności, której ten kod również nie zapewnia. –

5

I inna opcja, podobna do tej z Frank'a, ale z wykorzystanie zamknięć. I to szybciej niż wersja Franka.

bool isEven = true;    
var newList = list.Where(x => isEven = !isEven).ToList(); 
1
for (int i=myList.length-1; i >= 0; i--) 
    if (i % 2 == 0) 
    myList.Remove(myList[i]); 
0

chciałbym użyć standardowego wzoru powszechnie używany do kontenerów STL. Wykonaj usuń następnie przez wymazać.

ten sposób nie będzie t mylić ludzi, którzy są przyzwyczajeni do oglądania tego wzorca.

template<typename T> 
struct RemoveEven 
{ 
    RemoveEven():count(0) {} 
    bool operator()(T const&) 
    { 
     bool result = count%2 == 0; 
     count++; 
     return result; 
    } 
    private: 
     std::size_t count; 
}; 
int main() 
{ 
    std::list<int> a; 
    a.erase(std::remove_if(a.begin(),a.end(),RemoveEven<int>()),a.end()); 

} 
1

Oczywiście wykorzystanie uzależnione, ale można mieć IList otoki że mnoży indeks dajesz ją przez 2, a raporty długość listy, aby być 1/2 (szczegóły pomijana). To jest O (1).

+0

Sprytny! Ale kruche. –

2

Droga do Nirwany jest wybrukowana odroczoną egzekucją. Lub coś.

public static IEnumerable<T> AlternateItems<T>(this IEnumerable<T> source) 
    { 
     while (source.Any()) 
     { 
      yield return source.First(); 

      source = source.Skip(1); 

      if (source.Any()) source = source.Skip(1);     
     } 
    } 

Działa to dla wszystkich sekwencji, a nie tylko IList<>. Koszt iteracji jest odroczony do iteracji, co może być dużą wygraną, jeśli ostatecznie nie trzeba dotykać wszystkich elementów na liście.

W moich prostych testach, wydajność podczas iteracji na całej liście nie jest zbyt dobra, więc pamiętaj, aby profilować swoją rzeczywistą sytuację.