Fusion Tri d'une Liste Liée
J'ai été récemment brossage sur certains fondamentaux et trouvé de fusion et le tri d'une liste liée à un bon défi. Si vous avez une bonne mise en œuvre puis de le montrer ici.
Vous devez vous connecter pour publier un commentaire.
Me demande pourquoi il doit être grand défi, car il est dit ici, c'est une simple mise en œuvre en Java avec toutes les "trucs astucieux".
Plus simple/plus claire de la mise en œuvre pourrait être le récursive de la mise en œuvre, à partir de laquelle le NLog(N) le temps d'exécution est plus clair.
NB: Cela a un Log(N), les besoins de stockage de la récursion. Les performances devraient être à peu près comparable avec l'autre stratégie que j'ai posté. Il y a un potentiel d'optimisation ici par l'exécution de la fusion de la boucle while (list && droite), et simple en ajoutant le reste de la liste (puisque nous ne sommes pas vraiment à la fin de la liste; en sachant qu'ils sont fusionnés suffit).
Fortement basé sur l'EXCELLENT code à partir de: http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
Légèrement revue à la baisse, et rangée:
NB: C'est O(NLog(N)) de la garantie, et utilise O(1) ressources (pas de récursivité, pas de pile, rien).
Une façon intéressante de maintenir une pile, et seulement de fusion si la liste sur la pile a le même nombre d'éléments, et sinon, appuyez sur la liste, jusqu'à ce que vous exécutez à partir d'éléments dans la future liste, puis fusion de la pile.
Le plus simple est de
Gonnet + Baeza Yates Manuel d'Algorithmes. Vous l'appelez, avec le nombre d'éléments triés vous voulez, qui de manière récursive obtient traversée jusqu'à ce qu'il atteigne une demande pour une taille une liste qui vous puis il suffit de décoller le devant de la liste d'origine. Ces tous sont fusionnés en un grand triés liste.
[Notez que les frais de pile, l'un dans le premier post est appelé en Ligne de Mergesort et il obtient la moindre mention dans un exercice de Knuth Vol 3]
Voici une autre version récursive. Il n'est pas nécessaire à l'étape le long de la liste de le découper: nous fournissons un pointeur vers un élément de tête (qui ne fait pas partie de la sorte) et d'une longueur, et le récursive de la fonction retourne un pointeur vers la fin de la liste triée.
J'ai été obsédé par l'optimisation de l'encombrement de cet algorithme et voici ce que j'ai finalement arrivés au. Beaucoup d'autres code sur Internet et StackOverflow est horriblement mauvais. Il y a des gens qui essaient d'obtenir des point milieu de la liste, de faire de la récursivité, ayant de multiples boucles de gauche sur les nœuds, le maintien des comtes de tonne de choses - TOUT ce qui est inutile. MergeSort s'inscrit naturellement à la liste, et l'algorithme peut être beau et compact, mais il n'est pas trivial pour arriver à cet état.
Code ci-dessous maintient le nombre minimum de variables et a nombre minimum d'étapes logiques nécessaires pour l'algorithme (c'est à dire sans rendre le code difficile à maintenir/illisible) autant que je sache. Cependant, je n'ai pas essayé de minimiser la LDC et a gardé autant d'espaces que nécessaire pour garder les choses lisibles. J'ai testé ce code assez rigoureux tests unitaires.
Noter que cette réponse combine plusieurs techniques qui permettent de répondre à d'autres https://stackoverflow.com/a/3032462/207661. Alors que le code est en C#, il doit être facile à convertir en C++, Java, etc.
Points d'intérêt
Mise à jour: @ideasman42 a traduit de code C/C++ avec des suggestions pour la fixation des commentaires et des peu plus d'amélioration. Code ci-dessus est maintenant à jour avec ces.
p q & k
qui (je pense) doit êtreleft right & block_size
respectivement.J'ai décidé de tester les exemples ici, et aussi une autre approche, à l'origine écrit par Jonathan Cunningham dans le Pop-11. J'ai codé toutes les approches en C# et fait une comparaison avec une gamme de différents tailles. J'ai comparé les Mono eglib approche par Raja R Harinath, le code C# par Shital Shah, le Java approche par Jayadev, le récursives et non récursives versions par David Gamble, le premier du code C par Ed Wynn (cela s'est écrasé avec mon échantillon de données, je n'ai pas de débogage), et Cunningham version. Code complet ici: https://gist.github.com/314e572808f29adb0e41.git.
Mono eglib est basé sur une idée similaire à Cunningham et est comparable à celle de la vitesse, à moins que la liste se trouve être triés déjà, auquel cas Cunningham approche est beaucoup beaucoup plus rapide (si ses partiellement trié, le eglib est un peu plus rapide). Le eglib code utilise une table fixe de tenir la fusion sorte de récursivité, alors que Cunningham approche fonctionne en utilisant l'augmentation des niveaux de récursivité - de sorte qu'il commence à l'aide de pas de récursivité, puis 1-profondeur de récursivité, puis 2-profondeur de récursivité et ainsi de suite, en fonction de combien d'étapes sont nécessaires pour faire le tri. Je trouve le Cunningham code un peu plus facile à suivre et il n'y a pas de deviner impliqués dans la manière de big pour faire de la récursivité de la table, de sorte qu'il obtient mon vote. Les autres approches, j'ai essayé à partir de cette page ont été deux ou plus de deux fois plus lent.
Ici est le C# port de la Pop-11 tri:
Voici ma mise en œuvre de Knuth la "Liste de fusion de tri" (Algorithme 5.2.4 L de Vol.3 de TAOCP, 2ème ed.). Je vais ajouter quelques commentaires à la fin, mais voici un résumé:
Sur les graphes aléatoires, on roule un peu plus vite que Simon Tatham du code (voir Dave Gamble non-récursive de répondre, avec un lien), mais un peu plus lent que Dave Gamble récursive code. Il est plus difficile de comprendre que ce soit. Au moins dans mon implémentation, il exige que chaque élément pour avoir DEUX pointeurs vers des éléments. (Une autre solution serait d'un pointeur et d'un indicateur booléen.) Donc, ce n'est probablement pas une approche utile. Cependant, une caractéristique est qu'il court très vite si l'entrée a de longs tronçons qui sont déjà triées.
Il y a un non-récursive de liste liée mergesort dans mono eglib.
L'idée de base est que la boucle de contrôle pour les différents fusionne parallèle à la bit-à-bit-incrémentation d'un entier binaire. Il y a O(n) se confond à "insérer" n nœuds dans l'arbre de fusion, et le rang de ceux qui fusionne correspond au chiffre binaire qui est incrémenté. En utilisant cette analogie, seulement O(log n) nœuds de la fusion-arbre doivent être matérialisées en détention provisoire tableau.
thunk
argument ~ commeqsort_r
). Voir gist.github.com/ideasman42/...Un autre exemple de non-récursive de fusion de tri pour les listes chaînées, où les fonctions ne font pas partie d'une classe. Cet exemple de code et HP /Microsoft std::list::trier les deux utilisent le même algorithme de base. Un bas en haut, non-récursive, sorte de fusion qui utilise un de petite taille (de 26 à 32) tableau de pointeurs vers les premiers nœuds d'une liste de tableau[i] est soit 0 ou des points pour une liste de taille 2 à la puissance je. Sur mon système, Intel 2600K 3.4 ghz, il est possible de trier 4 millions de nœuds avec 32 bits entiers non signés que les données à environ 1 seconde.
C'est l'ensemble du Morceau de code qui montre comment nous pouvons créer une liste de liens en java et de les trier à l'aide de Fusion de tri. Je suis la création de nœud dans MergeNode classe et il y a une autre classe MergesortLinklist où il est de diviser et fusionner logique.
Je ne vois pas de C++ solutions posté ici. Donc, ici il va. Espérons que cela aide quelqu'un.
Ici est la Java de la mise en Œuvre de la Fusion de Tri sur la Liste chaînée:
while(fast != null && fast.next != null)
, n'est-il pas de provoquer une récursivité infinie pour une liste qui n'a que 2 éléments?Testé et de travail
C++
version unique de la liste liée, basé sur la plus haute voté réponse.singlelinkedlist.h:
main.cpp:
Plus Simple Implémentation Java: