2010-09-21 10 views
12

Vous avez une liste de n entiers et vous voulez le plus petit x. Par exemple,Trouver les x plus petits entiers dans une liste de longueur n

x_smallest([1, 2, 5, 4, 3], 3) doit renvoyer [1, 2, 3].

Je voterai pour des runtimes uniques dans des limites raisonnables et donnerai le chèque vert au meilleur temps d'exécution. Je vais commencer par O(n * x): Créer un tableau de longueur x. Itérer à travers la liste x fois, en tirant chaque fois le plus petit entier suivant.

Edits

  • Vous ne savez pas comment grand ou petit, ces chiffres sont à l'avance.
  • Vous ne vous souciez pas de la commande finale, vous voulez juste le plus petit x.
  • Ceci est déjà géré dans certaines solutions, mais disons que même si une liste unique n'est pas garantie, vous n'obtiendrez pas non plus de liste dégénérée comme [1, 1, 1, 1, 1].
+1

... est-ce un concours? – Randolpho

+0

Pourquoi structurez-vous la question comme une compétition? –

+4

Je ne sais pas, semblait être une façon amusante de le faire. –

Répondre

13

Vous pouvez trouver le plus petit élément k-ième O (n) l'heure. This has been discussed on StackOverflow before. Il existe des algorithmes randomisés relativement simples, tels que QuickSelect, qui s'exécutent en O (n) temps prévu et des algorithmes plus complexes qui s'exécutent en O (n) temps le plus défavorable.

Étant donné le k-ème élément le plus petit, vous pouvez faire un passage sur la liste pour trouver tous les éléments inférieurs au k-ème plus petit et vous avez terminé. (Je suppose que le tableau des résultats n'a pas besoin d'être trié.)

L'exécution globale est O (n).

+1

Cela suppose que les éléments sont uniques. Cela devient un peu plus compliqué car si le kth-élément n'est pas unique, votre sélection tombe en panne. Vous devez sélectionner tous les éléments inférieurs à kth-smallest, puis remplir le reste du tableau avec la valeur de kth-smallest. Je crois que la complexité reste la même. –

+0

Cela devient plus intéressant si vous voulez effectuer une sorte de sélection de préservation de l'ordre (par exemple, si vous avez vraiment des valeurs composées et n'en comparez qu'une partie, la clé, et que vous vous souciez de la charge utile). Vous pouvez toujours le faire en un seul passage à travers la majeure partie des données, en donnant O (kn) (qui tend à O (n) lorsque k₂n). –

+0

@ Mark Peters - d'accord. –

3

Ajoutez tous les n numéros à un tas et supprimez x d'entre eux. La complexité est O((n + x) log n). Puisque x est évidemment inférieur à n, c'est O(n log n).

+0

Pas besoin de garder tous les nombres dans le tas, juste le N le plus petit jusqu'ici. Laissez-moi développer cela. Utilisez un tas maximum. Ajouter un nombre Si le compte> N, supprime le premier élément du tas. –

+0

Oui, ça a été bien couvert par @Aaron alors je vais laisser cette réponse indépendante de ça. –

+0

La première solution 'O (n log n)' obtient 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

Le plus complet 'O (n * x)'. –

0

Dans le pseudo-code:

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, mais la liste ne vient pas triée. Le plus petit nombre entier peut sembler moyen à la fin. –

+0

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

+0

et le tri d'une liste d'éléments x est probablement assez rapide. – kriss

8

Tenir à jour la liste des x le plus élevé jusqu'à présent dans l'ordre de tri dans une liste de saut. Itérer à travers le tableau. Pour chaque élément, trouvez où il serait inséré dans la liste de saut (log x time). Si à l'intérieur de la liste, il s'agit de l'un des x les plus petits, insérez-le et supprimez l'élément à la fin de la liste. Sinon, ne faites rien.

temps O (n * log (x))

mise en œuvre alternative: maintenir la collection de x le plus élevé jusqu'à présent dans un maximum tas, comparer chaque nouvel élément avec élément supérieur du tas, et pop + insert nouvel élément uniquement si le nouvel élément est inférieur à l'élément supérieur. Puisque la comparaison à l'élément supérieur est O (1) et pop/insert O (log x), c'est aussi O (nlog (x))

+0

J'utiliserais probablement un [arbre de recherche binaire auto-équilibré] (http://en.wikipedia.org/wiki/Self-balancing_binary_search_tree) au lieu d'une liste de sauts, mais sinon, c'est ainsi que j'irais. – svick

+0

@svick: Le point de la liste de sélection est que les suppressions de la tête sont O (1). Bien sûr, la liste devrait être légèrement orientée à partir des descriptions d'Aaron afin que la valeur maximale soit en tête et la plus petite soit à la queue au lieu de l'inverse. Une suppression de la valeur maximale dans un BST serait O (log (x)) qui ne changerait pas la complexité globale, mais ajouterait certainement un facteur constant plus élevé. De plus, les schémas de rééquilibrage eux-mêmes sont parfois plus complexes que de relier un nœud dans une liste. Cependant, je me demande s'il y a une manière intelligente de faire ceci avec un arbre de splay? –

3

Si la plage de nombres (L) est connue, vous pouvez faire une modification genre de comptage.

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 

Cela a une durée d'exécution de O (n + L) (avec des frais généraux de mémoire de O (L)) ce qui le rend très attractif si la plage est petite (L < n log n).

+0

Je vais changer cela. Cependant, permettez-moi de préciser que la gamme des nombres entiers n'est pas connue. –

+2

Si ce n'est pas connu, vous pouvez toujours faire un seul passage sur la liste en O (n) time pour déterminer L, puis décider si cela vaut la peine de le faire ou non. –

+0

Un autre bon point. En outre, vous avez décrit la plage appropriée. Gloire. –

0

Vous pouvez trier puis prendre les premières valeurs x?

Java: avec 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);  
    } 

} 

Son assez rapide.

+1

La plus complète des réponses de fruits à portée de main. –

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

En regardant la complexité: O (x + (nx) * x) - en supposant x est une constante, 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 

Le pire cas est O (x * n), mais sera typiquement plus proche de O (n).

0

Qu'en est-il de l'utilisation d'un splay tree? En raison de l'approche unique de l'équilibrage adaptatif de l'arborescence splay, l'implémentation de l'algorithme est simple, avec l'avantage supplémentaire de pouvoir énumérer par la suite les éléments x. Voici un pseudo code.

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

Les GetLargest et Remove opérations ont une complexité amortie de O (log (n)), mais parce que le dernier élément accessible des bulles vers le haut, il serait normalement O (1). La complexité de l'espace est donc O (x) et la complexité d'exécution est O (n * log (x)). Si le tableau arrive à être déjà commandé alors cet algorithme obtiendrait sa meilleure complexité de O (n) avec un tableau ordonné ascendant ou descendant. Cependant, un ordre très étrange ou particulier pourrait entraîner une complexité O (n^2). Pouvez-vous deviner comment le tableau devrait être commandé pour que cela se produise?

+0

Intéressant. Je n'avais jamais entendu parler d'un arbre splay. Je crois que vous vouliez dire 'if (array [i]

+0

@Dave: Oui, corrigé! –

0

SCALA, et probablement d'autres langages fonctionnels, une évidence:

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