2010-10-01 2 views
8

У меня есть список:Подсчет повторяющихся чисел в списке

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

Теперь мне нужно сделать программу, которая вычисляет двойные числа. Число удваивается, когда число до того, как оно будет одинаковым. Надеюсь, вы понимаете. Так что 1 удваивается, 4 удваивается, и мы получили 2 двойных в 7,7,7.

+1

Можете ли вы повести немного больше информации о том, почему и как? Чувствует себя вопросом домашней работы/интервью. Было бы очень просто решить только использование цикла и сравнить предыдущее с текущим и сохранить/сбросить, если совпадение найдено - всего лишь немного дополнительной логики, чтобы остановить 7, считая 3 раза. Если вы хотите, чтобы это было решено в linq или что-то, то это более интересно. –

ответ

26

Вот решение в LINQ:

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

Это создает другую последовательность, пропуская первый элемент списка, а затем находит элементы из обеих последовательностей, которые имеют одинаковый индекс и то же значение. Он будет работать в линейном времени, но только потому, что список предлагает O(1) доступ по индексу.

+2

Определенно +1. Ответ краток, правилен (не проверен, но я беру на себя риск), очень умный и правильно утверждал, почему он работает в линейном времени. – Fede

+2

+1: Это очень элегантно! – RedFilter

+9

Представьте, что вы копируете это как домашний ответ, а затем должны объяснить его классу (и учителю) ... mwahahaha –

2

что-то подобное может работать:

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

EDIT:

приведенный выше код не получает результат ОП хотел. Вот отредактированная версия, которая берет вдохновение от элегантного решения Ани ниже: :)

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

Вот подход, который является относительно простым, только перебирает раз над последовательностью, и работает с любой последовательностью (не только перечислены):

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; 
     } 
    } 
} 

Вот еще один, который еще проще в том, что это только запрос LINQ, но он использует побочные эффекты в предложении Where, что противно:

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; 
}); 

Третья альтернатива, которая несколько чище, но использует SelectConsecutive метод, который является в основном SelectPairs от this answer, но переименовали быть немного понятнее :)

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

Что значит «использует побочный эффект»? –

+2

У меня глаза кровоточат. –

+0

@ Lasse: Хорошая точка. Я изменил свой план. Подождите, и я верну бонусная версия :) –

6

Все, кажется, пытается найти хорошие способы сделать это, так здесь действительно плохой способ:

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; 

Пожалуйста, не делайте этого так.

+0

haha, +1 для чувства юмора. В пятницу в конце концов –

+0

Спасибо. Я не был уверен, удастся ли мне голосовать за сообщение плохого ответа или голосов за то, что он сказал, что это плохо. :-) – teedyay

+0

+1 Это не совсем плохой ответ не Linq, кроме исключения - вы можете использовать ContainsKey или TryGetValue, чтобы избежать исключения, и это было бы прекрасно. –

0

Здесь вы идете с ответом в 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

Пример, который (вероятно) работает лучше, чем при использовании Linq, хотя, возможно, менее элегантен:

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

Вы можете сделать это :

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

Это немного похоже @ KJN отвечают, за исключением того, я думаю, что он выражает «двойников» и «два дубли» раздел в этом вопросе немного лучше:

  1. группа всех целых чисел вместе
  2. интересуют только те, которые появляются более одного раза (g.Count() > 1)
  3. выберите уплощенную список «двойников», будучи тем, после того, как первый (g.Skip(1))

PS: Мы предполагаем здесь, что GroupBy не первый сортировки списка и если это произойдет, то что-то не негативное влияние предварительного отсортированного списка ...

Смежные вопросы