2010-10-01 17 views
8

Mam listę:Liczenie duplikatów numerów na liście

int list = { 1,1,2,3,4,4,5,7,7,7,10}; 

Teraz muszę zrobić program, który oblicza podwójne numery. Liczba jest podwójna, gdy liczba przed nim jest taka sama. Mam nadzieję że rozumiesz. Więc 1 jest podwójne, 4 oznacza podwójne, a my 2 razy w 7,8,7.

+1

Czy możesz podać trochę więcej informacji, dlaczego? Czujesz się jak zadanie domowe/wywiad. Byłoby bardzo łatwo rozwiązać tylko za pomocą pętli i porównać poprzedni do bieżącego i zapisać/zresetować, jeśli zostanie znaleziony mecz - tylko trochę dodatkowej logiki, aby zatrzymać 7 liczone 3 razy. Jeśli chcesz go rozwiązać w Linq lub coś w tym stylu, to jest to bardziej interesujące. –

Odpowiedz

26

Oto rozwiązaniem LINQ:

var doubles = list.Skip(1) 
        .Where((number, index) => list[index] == number); 

Stwarza inną sekwencję pomijając pierwszy element listy, a następnie stwierdza, elementy z obu sekwencjach, które mają taki sam współczynnik i tę samą wartość. Będzie działał w czasie liniowym, ale tylko dlatego, że lista oferuje dostęp przez indeks w postaci O(1).

+2

Zdecydowanie +1. Odpowiedź jest zwięzła, poprawna (nie testowana, ale podejmuję ryzyko), bardzo sprytna i poprawnie argumentowała, dlaczego działa w czasie liniowym. – Fede

+2

+1: To bardzo eleganckie! – RedFilter

+9

Wyobraź sobie, że skopiujesz to jako odpowiedź na zadanie domowe, a następnie musisz wyjaśnić je klasie (i nauczycielowi) ... mwahahaha –

2

coś takiego może działać:

list.GroupBy (l => l).Where (l => l.Count() > 1).SelectMany (l => l).Distinct(); 

EDIT:

powyższy kod nie uzyskać wynik PO chciała. Oto edytowany wersja, która wzorując się eleganckie rozwiązanie Ani jest poniżej: :)

list.GroupBy(l => l).Select(g=>g.Skip(1)).SelectMany (l => l); 
7

Oto podejście, które jest stosunkowo prosta, tylko iteracje raz na sekwencji i współpracuje z dowolnej kolejności (nie tylko wymienia):

public IEnumerable<T> FindConsecutiveDuplicates<T>(this IEnumerable<T> source) 
{ 
    using (var iterator = source.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      yield break; 
     } 
     T current = iterator.Current; 
     while (iterator.MoveNext()) 
     { 
      if (EqualityComparer<T>.Default.Equals(current, iterator.Current)) 
      { 
       yield return current; 
      } 
      current = iterator.Current; 
     } 
    } 
} 

Oto jeszcze jeden, który jest jeszcze prostsza, że ​​to tylko zapytań LINQ, ale korzysta z efektów ubocznych w klauzuli WHERE, która jest brzydki:

IEnumerable<int> sequence = ...; 

bool first = true; 
int current = 0; 
var result = sequence.Where(x => { 
    bool result = !first && x == current; 
    current = x; 
    first = false; 
    return result; 
}); 

Trzecia alternatywa, która jest nieco czystsze, ale używa SelectConsecutive metodę, która jest w zasadzie SelectPairs z this answer, ale przemianowany być nieco jaśniejsze :)

IEnumerable<int> sequence = ...; 
IEnumerable<int> result = sequence.SelectConsecutive((x, y) => new { x, y }) 
            .Where(z => z.x == z.y); 
+2

Co masz na myśli "używa efektu ubocznego"? –

+2

Moje oczy krwawią. –

+0

@Lasse: Dobra uwaga. Um, zmieniłem mój plan. Trzymaj się, a ja dodam wersję poboczną :) –

6

Każdy wydaje się być próbuje znaleźć dobre sposoby to robić, więc oto naprawdę zły sposób:

List<int> doubles = new List<int>(); 
Dictionary<int, bool> seenBefore = new Dictionary<int, bool>(); 

foreach(int i in list) 
{ 
    try 
    { 
     seenBefore.Add(i, true); 
    } 
    catch (ArgumentException) 
    { 
     doubles.Add(i); 
    } 
} 

return doubles; 

Proszę, nie rób tego w ten sposób.

+0

haha, +1 za poczucie humoru. Jest piątek, po tym wszystkim. –

+0

Dzięki. Nie byłam pewna, czy dostanę głosowanie za opublikowanie złej odpowiedzi lub głosów w górę za stwierdzenie, że było źle. :-) – teedyay

+0

+1 Nie jest to całkowicie zła odpowiedź nie Linq oprócz wyjątku - można użyć ContainsKey lub TryGetValue, aby uniknąć wyjątku i byłoby dobrze. –

0

Tutaj można przejść z odpowiedzią w C# :)

int[] intarray = new int[] { 1, 1, 2, 3, 4, 4, 5, 7, 7, 7, 10 }; 

int previousnumber = -1; 
List<int> doubleDigits = new List<int>(); 
for (int i = 0; i < intarray.Length; i++) 
{ 
    if (previousnumber == -1) { previousnumber = intarray[i]; continue; } 
    if (intarray[i] == previousnumber) 
    { 
     if (!doubleDigits.Contains(intarray[i])) 
     { 
      doubleDigits.Add(intarray[i]); 
      //Console.WriteLine("Duplicate int found - " + intarray[i]); 
      continue; 
     } 
    } 
    else 
    { 
     previousnumber = intarray[i]; 
    } 
} 
0

Przykładem, że (prawdopodobnie) wykonuje lepiej niż przy użyciu LINQ, choć jest niewątpliwie mniej elegancki:

for (int i = 1; i < list.Count; i++) 
    if (list[i] == list[i - 1]) 
     doubles.Add(list[i]); 
0

Można to zrobić :

list.GroupBy(i => i).Where(g => g.Count() > 1).SelectMany(g => g.Skip(1)) 

to jest trochę jak na @ KJN odpowiedź, z wyjątkiem myślę, że wyraża „podwójne” i „dwa dwuosobowe” klauzula w pytaniu nieco lepiej:

  1. grupa wszystkie liczby całkowite razem
  2. zainteresowany tylko w tych, które pojawiają się więcej niż jeden raz (g.Count() > 1)
  3. wybrać spłaszczoną listę „sobowtórów”, jako tych, po pierwszym (g.Skip(1))

PS: zakładamy tutaj, że nie GroupBy najpierw posortować listę, a jeśli tak jest, że tego rodzaju nie jest negatywnie wpływa wstępnie posortowane liście ...

Powiązane problemy