2010-09-21 26 views
12

Hai una lista di n interi e vuoi la x più piccola. Ad esempio,Trova x i numeri interi più piccoli in un elenco di lunghezza n

x_smallest([1, 2, 5, 4, 3], 3) deve restituire [1, 2, 3].

Voterò su runtime unici all'interno della ragione e assegnerò il controllo verde al miglior runtime.

Inizierò con O(n * x): creare un array di lunghezza x. Scorrere la lista x volte, ogni volta tirando fuori il numero più piccolo successivo.

modifiche

  • non avete idea di quanto grande o piccolo questi numeri sono davanti a tempo.
  • Non ti interessa l'ordine finale, vuoi solo la x più piccola.
  • Questo è già stato gestito in alcune soluzioni, ma diciamo che anche se non è garantito un elenco univoco, non si otterrà neanche un elenco degenerato come [1, 1, 1, 1, 1].
+1

... questo è un concorso? – Randolpho

+0

Perché stai strutturando la domanda come una competizione? –

+4

Non so, mi è sembrato un modo divertente per farlo. –

risposta

13

È possibile trovare il più piccolo elemento k-esimo in O (n) tempo. This has been discussed on StackOverflow before. Esistono algoritmi randomizzati relativamente semplici, come QuickSelect, che vengono eseguiti in tempo O (n) e algoritmi più complicati eseguiti in tempo O (n) nel caso peggiore.

Dato l'elemento k-esimo più piccolo è possibile effettuare un passaggio sull'elenco per trovare tutti gli elementi inferiori al k-esimo più piccolo e il gioco è fatto. (Suppongo che l'array di risultati non debba essere ordinato.)

Il tempo di esecuzione complessivo è O (n).

+1

Ciò presuppone che gli elementi siano unici. Diventa un po 'più complicato dal momento che se l'elemento kth non è unico, la selezione si interrompe. Dovresti selezionare qualsiasi elemento minore del kth-più piccolo, e quindi riempire il resto dell'array con il valore del kth-più piccolo. Credo che la complessità rimanga la stessa. –

+0

Diventa più interessante se desideri eseguire una sorta di selezione di conservazione degli ordini (ad es., Se hai davvero ottenuto valori composti e stai solo confrontando parte di essi, la chiave, e tuttavia tieni conto del carico utile). Puoi comunque farlo in un unico passaggio attraverso la maggior parte dei dati, dando O (kn) (che tende a O (n) quando k Chiesan). –

+0

@ Mark Peters - d'accordo. –

3

Aggiungi tutti i numeri in un heap ed elimina x di essi. La complessità è O((n + x) log n). Poiché x è ovviamente minore di n, è O(n log n).

+0

Non è necessario conservare tutti i numeri nell'heap, solo il N minimo finora. Permettetemi di espanderlo. Utilizza un heap massimo. Aggiungi un numero. Se il conteggio> N, quindi rimuovere il primo elemento dall'heap. –

+0

Sì, questo è stato coperto bene da @Aaron, quindi lascerò questa risposta indipendente da quello. –

+0

La prima soluzione 'O (n log n)' ottiene un upvote. –

0

Psudocode:

def x_smallest(array<int> arr, int limit) 
    array<int> ret = new array[limit] 

    ret = {INT_MAX} 

    for i in arr 
     for j in range(0..limit) 
      if (i < ret[j]) 
       ret[j] = i 
      endif 
     endfor 
    endfor 

    return ret 
enddef 
+0

Il più completo 'O (n * x)'. –

0

In pseudo-codice:

y = length of list/2 

if (x > y) 
    iterate and pop off the (length - x) largest 
else 
    iterate and pop off the x smallest 

O (n/2 * x)?

+0

Ah, ma l'elenco non viene ordinato. Il numero più piccolo potrebbe apparire alla fine. –

+0

e O (n/2 * x) = O (n * x) – aaronasterling

+0

e l'ordinamento di un elenco di elementi x è probabilmente veloce. – kriss

8

gestire l'elenco delle x più alto finora in modo ordinato in una skip list. Scorrere l'array. Per ogni elemento, trova dove verrà inserito nell'elenco skip (tempo x log). Se all'interno della lista, è una delle x più piccole finora, quindi inseriscila e rimuovi l'elemento alla fine dell'elenco. Altrimenti non fare nulla.

Tempo O (n * log (x))

realizzazione alternativa: mantenere la collezione di x più alto finora in un max-heap, confrontare ogni nuovo elemento con elemento superiore del mucchio, e pop + inserto nuovo elemento solo se il nuovo elemento è inferiore all'elemento superiore. Poiché il confronto con l'elemento superiore è O (1) e pop/insert O (log x), questo è anche O (nlog (x))

+0

Probabilmente userei un [albero di ricerca binaria autobilanciante] (http://en.wikipedia.org/wiki/Self-balancing_binary_search_tree) invece di una lista di salti, ma altrimenti, questo è il modo in cui andrei. – svick

+0

@svick: il punto dell'elenco dei salti è che le rimozioni dalla testa sono O (1). Ovviamente, la lista dovrebbe essere leggermente orientata dalle descrizioni di Aaron in modo che il valore massimo sia alla testa e il più piccolo alla coda invece del contrario. Una rimozione del valore massimo in un BST sarebbe O (log (x)) che non cambierebbe la complessità complessiva, ma aggiungerebbe sicuramente un fattore costante più elevato. Inoltre, gli schemi di ribilanciamento stessi sono a volte più complessi di ricollegare un nodo in una lista. Tuttavia, mi chiedo se c'è un modo intelligente per farlo con uno splay tree? –

3

Se l'intervallo di numeri (L) è noto, è possibile eseguire una modifica conteggio sort.

given L, x, input[] 
counts <- array[0..L] 
for each number in input 
    increment counts[number] 
next 

#populate the output 
index <- 0 
xIndex <- 0 
while xIndex < x and index <= L 
    if counts[index] > 0 then 
     decrement counts[index] 
     output[xIndex] = index 
     increment xIndex 
    else 
     increment index 
    end if 
loop 

Questo ha un tempo di esecuzione di O (n + L) (con sovraccarico di memoria di O (L)) che lo rende piuttosto interessante se il campo è piccolo (L < n log n).

+0

Lo manterrò. Tuttavia, permettimi di chiarire che l'intervallo di numeri interi non è noto. –

+2

Se non è noto, puoi ancora fare un singolo passaggio sulla lista in O (n) tempo per determinare L, e poi decidere se vale la pena farlo in questo modo o no. –

+0

Un altro buon punto. Inoltre, hai descritto l'intervallo appropriato. Complimenti. –

0

È possibile ordinare quindi prendere i primi valori x?

Java: con QuickSort O (n log n)

import java.util.Arrays; 
import java.util.Random; 

public class Main { 

    public static void main(String[] args) { 
     Random random = new Random(); // Random number generator 
     int[] list = new int[1000]; 
     int lenght = 3; 

     // Initialize array with positive random values 
     for (int i = 0; i < list.length; i++) { 
      list[i] = Math.abs(random.nextInt()); 
     } 

     // Solution 
     int[] output = findSmallest(list, lenght); 

     // Display Results 
     for(int x : output) 
      System.out.println(x); 
    } 

    private static int[] findSmallest(int[] list, int lenght) { 
     // A tuned quicksort 
     Arrays.sort(list); 
     // Send back correct lenght 
     return Arrays.copyOf(list, lenght);  
    } 

} 

La sua piuttosto veloce.

+1

La risposta più approfondita dei frutti a frutto basso. –

0
private static int[] x_smallest(int[] input, int x) 
    { 
     int[] output = new int[x]; 
     for (int i = 0; i < x; i++) { // O(x) 
      output[i] = input[i]; 
     } 

     for (int i = x; i < input.Length; i++) { // + O(n-x) 
      int current = input[i]; 
      int temp; 

      for (int j = 0; j < output.Length; j++) { // * O(x) 
       if (current < output[j]) { 
        temp = output[j]; 
        output[j] = current; 
        current = temp; 
       } 
      } 
     } 

     return output; 
    } 

Guardando la complessità: O (x + (NX) * x) - supponendo che x è una costante, O (n)

1
def x_smallest(items, x): 
    result = sorted(items[:x]) 
    for i in items[x:]: 
     if i < result[-1]: 
      result[-1] = i 
      j = x - 1 
      while j > 0 and result[j] < result[j-1]: 
       result[j-1], result[j] = result[j], result[j-1] 
       j -= 1 
    return result 

caso peggiore è O (x * n), ma sarà in genere più vicino a O (n).

0

Che ne è dell'utilizzo di splay tree? Grazie all'esclusivo approccio dello splay tree al bilanciamento adattivo, consente un'implementazione rapida dell'algoritmo con l'ulteriore vantaggio di essere in grado di enumerare gli articoli x in ordine successivo. Ecco alcuni psuedocode.

public SplayTree GetSmallest(int[] array, int x) 
{ 
    var tree = new SplayTree(); 
    for (int i = 0; i < array.Length; i++) 
    { 
    int max = tree.GetLargest(); 
    if (array[i] < max || tree.Count < x) 
    { 
     if (tree.Count >= x) 
     { 
     tree.Remove(max); 
     } 
     tree.Add(array[i]); 
    } 
    } 
    return tree; 
} 

I GetLargest e Remove operazioni hanno una complessità ammortizzato di O (log (n)), ma perché l'ultimo elemento accede bolle all'inizio sarebbe normalmente O (1). Quindi la complessità dello spazio è O (x) e la complessità del tempo di esecuzione è O (n * log (x)). Se la matrice dovesse già essere ordinata, questo algoritmo otterrebbe la sua migliore complessità del caso di O (n) con una matrice ordinata crescente o decrescente. Tuttavia, un ordinamento molto strano o peculiare potrebbe comportare una complessità O (n^2). Riuscite a indovinare come dovrebbe essere ordinato l'array affinché ciò accada?

+0

Interessante. Non avevo mai sentito parlare di uno Splay Tree. Credo che tu intendessi "if (array [i]

+0

@Dave: Sì, corretto! –

0

a Scala, e probabilmente altri linguaggi funzionali, una bazzecola:

scala> List (1, 3, 6, 4, 5, 1, 2, 9, 4) sortWith (_<_) take 5 
res18: List[Int] = List(1, 1, 2, 3, 4) 
Problemi correlati