2010-03-19 3 views
39

У меня есть List<int>, который содержит, например, 1,2,4,7,9.Проверьте наличие недостающего числа

У меня есть диапазон от 0 до 10.

Есть ли способ, чтобы определить, какие цифры отсутствуют в этой последовательности?

Я думал, LINQ может предоставить вариант, но я не могу увидеть один

В реальном мире мой список может содержать 100000 элементов таким образом производительность является ключевым

+2

Как указано в комментарии к Андраш, в диапазоне от 1 млн Интс, и список 100000, используя за исключением (на моей машине) занимает 0,25 секунды. Но мы не знаем, насколько это достаточно для вас (а если нет, то допустимые пределы производительности?) –

ответ

94
var list = new List<int>(new[] { 1, 2, 4, 7, 9 }); 
var result = Enumerable.Range(0, 10).Except(list); 
+7

+1 простой и чистый –

+2

Определенно простой и чистый, но скорость связана с большими диапазонами –

+0

Могу ли я получить это 2.0 please – Vivekh

11

Включите диапазон, который вы хотите проверить в HashSet:

public IEnumerable<int> FindMissing(IEnumerable<int> values) 
{ 
    HashSet<int> myRange = new HashSet<int>(Enumerable.Range(0,10)); 
    myRange.ExceptWith(values); 
    return myRange; 
} 

будет возвращать значения, которые не values.

+0

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

+0

'HashSet . МетодExceptWith' не возвращает значение (http://msdn.microsoft.com/en-us/library/bb299875.aspx). Он фактически преобразует исходный хеш. –

+0

Имейте образец кода обновления - только что понял, что, когда вы разместили этот комментарий! –

4

Метод LINQ Except был бы наиболее читаемым. Независимо от того, выполняет ли он вас адекватно или нет, это будет вопросом для тестирования.

E.g.

range.Except(listOfValues); 

Редактировать

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

static void Main() 
{ 
    var a = Enumerable.Range(0, 1000000); 
    var b = new List<int>(); 

    for (int i = 0; i < 1000000; i += 10) 
    { 
     b.Add(i); 
    } 

    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 
    var c = a.Except(b).ToList(); 
    sw.Stop(); 
    Console.WriteLine("Milliseconds {0}", sw.ElapsedMilliseconds); 
    sw.Reset(); 

    Console.ReadLine(); 
} 
+1

Больше, чем на полпути. Вместо того, чтобы просто указывать контрольный код, вы также можете опубликовать свои результаты. На вопрос был дан ответ, но для других, которые приходят и интересуются скоростью по сравнению с «foreach», например, ваш ответ становится полезным, если у вас есть результаты. –

-1

Создать массив элементов пит

const int numItems = 1000; 
bool found[numItems] = new bool[numItems]; 


List<int> list; 

PopulateList(list); 

list.ForEach(i => found[i] = true); 

// now iterate found for the numbers found 
for(int count = 0; i < numItems; ++numItems){ 
    Console.WriteList("Item {0} is {1}", count, found[count] ? "there" : "not there"); 
} 
+0

Что случилось с этим ответом? –

+0

1. Вы вынуждаете использовать объект List <>; 2. Вы создаете очень большой промежуточный объект (ваш массив bools); 3. Это не чистое и понятное решение, посмотрите на другие. –

+3

Спасибо. Я не возражаю против того, что ответ проголосовал за меня. Я просто хочу, чтобы мне изначально рассказали причины, чтобы я мог учиться. Еще раз спасибо –

2
 List<int> selectedNumbers = new List<int>(){8, 5, 3, 12, 2}; 

     int firstNumber = selectedNumbers.OrderBy(i => i).First(); 
     int lastNumber = selectedNumbers.OrderBy(i => i).Last(); 

     List<int> allNumbers = Enumerable.Range(firstNumber, lastNumber - firstNumber + 1).ToList(); 

     List<int> missingNumbers = allNumbers.Except(selectedNumbers).ToList(); 

     foreach (int i in missingNumbers) 
     { 
      Response.Write(i); 
     } 
2

Если диапазон предсказуем я предлагаю следующее решение:

public static void Main() 
{ 
    //set up the expected range 
    var expectedRange = Enumerable.Range(0, 10); 

    //set up the current list 
    var currentList = new List<int> {1, 2, 4, 7, 9}; 

    //get the missing items 
    var missingItems = expectedRange.Except(currentList);  

    //print the missing items 
    foreach (int missingItem in missingItems) 
    { 
     Console.WriteLine(missingItem); 
    } 

    Console.ReadLine(); 
} 

С уважением, y00daa

1

Это не использует LINQ, но он работает в линейном времени.

Я предполагаю, что список ввода отсортирован.

Это занимает O(list.Count).

private static IEnumerable<int> get_miss(List<int> list,int length) 
{ 
    var miss = new List<int>(); 
    int i =0; 
    for (i = 0; i < list.Count - 1; i++) 
    { 
     foreach (var item in 
        Enumerable.Range(list[i] + 1, list[i + 1] - list[i] - 1)) 
     { 
      yield return item; 
     } 

    } 
    foreach (var item in Enumerable.Range(list[i]+1,length-list[i])) 
    { 
     yield return item; 
    } 
} 

Это займет O(n) где п длина полного диапазона.

static void Main() 
    { 
     List<int> identifiers = new List<int>() { 1, 2, 4, 7, 9 }; 

     Stopwatch sw = new Stopwatch(); 

     sw.Start(); 
     List<int> miss = GetMiss(identifiers,150000); 
     sw.Stop(); 
     Console.WriteLine("{0}",sw.ElapsedMilliseconds); 

    } 
private static List<int> GetMiss(List<int> identifiers,int length) 
{ 
    List<int> miss = new List<int>(); 

    int j = 0; 

    for (int i = 0; i < length; i++) 
    { 
     if (i < identifiers[j]) 
      miss.Add(i); 

     else if (i == identifiers[j]) 
      j++; 

     if (j == identifiers.Count) 
     { 
      miss.AddRange(Enumerable.Range(i + 1, length - i)); 
      break; 
     } 
    } 

    return miss; 
} 
1

Хорошо, создайте новый список, который будет параллелен исходному списку и запустит метод За исключением этого ...

Я создал полностью LinQ ответ, используя метод Aggregate вместо того, чтобы найти missings:

var list = new List<int>(new[] { 1, 2, 4, 7, 9 }); // Assumes list is ordered at this point 

list.Insert(0, 0); // No error checking, just put in the lowest and highest possibles. 
list.Add(10);  // For real world processing, put in check and if not represented then add it/them. 

var missing = new List<int>(); // Hold any missing values found. 

list.Aggregate ((seed, aggr) => // Seed is the previous #, aggr is the current number. 
{ 
    var diff = (aggr - seed) -1; // A difference between them indicates missing. 

    if (diff > 0)     // Missing found...put in the missing range. 
     missing.AddRange(Enumerable.Range((aggr - diff), diff)); 

    return aggr;  
}); 

недостающее список имеет это после того, как приведенный выше код был выполнен:

3, 5, 6, 8 
1

Альтернативный метод, который работает в целом для любой два IEnunumerable<T>, где T :IComparable. Если IEnumerables отсортированы, это работает в O (1) памяти (т. Е. Нет другого ICollection и вычитания и т. Д.) И в O (n) время.

Использование IEnumerable<IComparable> и GetEnumerator делает это немного менее читаемым, но гораздо более общим.

Реализация

/// <summary> 
/// <para>For two sorted IEnumerable&lt;T&gt; (superset and subset),</para> 
/// <para>returns the values in superset which are not in subset.</para> 
/// </summary> 
public static IEnumerable<T> CompareSortedEnumerables<T>(IEnumerable<T> superset, IEnumerable<T> subset) 
    where T : IComparable 
{ 
    IEnumerator<T> supersetEnumerator = superset.GetEnumerator(); 
    IEnumerator<T> subsetEnumerator = subset.GetEnumerator(); 
    bool itemsRemainingInSubset = subsetEnumerator.MoveNext(); 

    // handle the case when the first item in subset is less than the first item in superset 
    T firstInSuperset = superset.First(); 
    while (itemsRemainingInSubset && supersetEnumerator.Current.CompareTo(subsetEnumerator.Current) >= 0) 
     itemsRemainingInSubset = subsetEnumerator.MoveNext(); 

    while (supersetEnumerator.MoveNext()) 
    { 
     int comparison = supersetEnumerator.Current.CompareTo(subsetEnumerator.Current); 
     if (!itemsRemainingInSubset || comparison < 0) 
     { 
      yield return supersetEnumerator.Current; 
     } 
     else if (comparison >= 0) 
     { 
      while (itemsRemainingInSubset && supersetEnumerator.Current.CompareTo(subsetEnumerator.Current) >= 0) 
       itemsRemainingInSubset = subsetEnumerator.MoveNext(); 
     } 
    } 
} 

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

var values = Enumerable.Range(0, 11); 
var list = new List<int> { 1, 2, 4, 7, 9 }; 
var notIncluded = CompareSortedEnumerables(values, list); 
Смежные вопросы