2010-03-19 19 views
39

Ho un List<int> che contiene 1,2,4,7,9 per esempio.Verifica il numero mancante in sequenza

Ho un range da 0 a 10.

C'è un modo per determinare quali numeri mancanti in quella sequenza?

ho pensato LINQ potrebbe fornire una possibilità, ma non riesco a vedere uno

Nel mondo reale il mio elenco potrebbe contenere 100.000 articoli quindi le prestazioni è la chiave

+2

Come indicato in un commento ad Andras, un intervallo di 1 milione di int, e un elenco di 100.000, con eccezione (sulla mia macchina) richiede 0,25 secondi. Ma non abbiamo modo di sapere se questo è abbastanza performante per te (e se no, quali sono i limiti accettabili per le prestazioni?) –

risposta

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

+1 semplice e pulito –

+2

Sicuramente semplice e pulito - ma problemi di velocità se le gamme sono grandi –

+0

Posso avere questo 2.0 per favore – Vivekh

11

Ruotare l'intervallo che si desidera controllare in un HashSet:

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

restituirà i valori che non sono in values.

+0

Il motivo per cui ho optato per questo è con sequenze di grandi numeri questo dovrebbe essere più veloce come l'hashset viene ridotto iterando attraverso l'elenco di input e rimuovendo ogni elemento. Il metodo di esclusione dell'iterazione itera la raccolta di input per ciascun elemento della raccolta. –

+0

'HashSet . Il metodo ExceptWith' non restituisce un valore (http://msdn.microsoft.com/en-us/library/bb299875.aspx). In realtà trasforma l'hash originale. –

+0

Esegui l'esempio del codice di aggiornamento: ti sei appena reso conto che come hai postato quel commento! –

4

Il metododi LINQ è il più leggibile. Se si esegue adeguatamente per te o no, sarebbe una questione di test.

E.g.

range.Except(listOfValues); 

Modifica

Ecco il programma che ho usato per il mio mini-punto di riferimento, per gli altri a collegare via con:

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

Più di metà strada. Invece di pubblicare semplicemente il codice di riferimento, potresti pubblicare anche i tuoi risultati. Alla domanda è stata data risposta, ma per gli altri che arrivano e sono curiosi sulla velocità rispetto a un 'foreach', ad esempio la tua risposta diventa utile se hai dei risultati. –

-1

Creare un array di oggetti num

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

cosa c'è di sbagliato in questa risposta? –

+0

1. Si sta forzando l'uso di un oggetto List <>; 2. Stai creando un oggetto intermedio molto grande (la tua matrice di bool); 3. Non è una soluzione chiara e leggibile, guarda gli altri. –

+3

Grazie. Non mi dispiace se la risposta è stata votata, vorrei solo che all'inizio mi fossero state spiegate le ragioni per cui avrei potuto imparare. Grazie ancora –

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

Se l'intervallo è prevedibile propongo la seguente soluzione:

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

saluti, y00daa

1

Questo non fa uso di LINQ ma funziona in tempo lineare.

Suppongo che l'elenco di input sia ordinato.

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

Questo dovrebbe prendere O(n) dove n è la lunghezza della gamma completa.

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

Ok, davvero, creare un nuovo elenco che è parallelo all'elenco iniziale ed eseguire il metodo Tranne su di esso ...

ho creato una completamente LINQ risposta utilizzando il metodo Aggregate invece di trovare le mancanze:

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

La lista mancante ha questo dopo che il codice di cui sopra è stato eseguito:

3, 5, 6, 8 
1

Un metodo alternativo che funziona in generale per qualsiasi due IEnunumerable<T> dove T :IComparable. Se gli oggetti IEnumerables sono entrambi ordinati, questo funziona nella memoria O (1) (vale a dire non viene creata un'altra ICollection e sottrazione, ecc.) E nell'orario O (n).

L'utilizzo di IEnumerable<IComparable> e GetEnumerator lo rende un po 'meno leggibile, ma molto più generale.

Attuazione

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

Uso

var values = Enumerable.Range(0, 11); 
var list = new List<int> { 1, 2, 4, 7, 9 }; 
var notIncluded = CompareSortedEnumerables(values, list); 
Problemi correlati