1 Brève introduction
HeapSelect est un algorithme permettant de sélectionner le Kème plus grand élément d'un tableau. C'est une variante du problème de sélection qui consiste à trouver un élément spécifique dans un ensemble non ordonné ou partiellement ordonné.
Aperçu de l'algorithme : le tableau est converti en un tas maximum, puis le nœud racine est supprimé à plusieurs reprises et remplacé par l'élément le plus grand suivant jusqu'à ce que le Kème élément le plus grand soit trouvé.
2 tasselect
Nous avons vu un algorithme randomisé avec une comparaison n + O (log n) attendue. Pouvons-nous obtenir les mêmes performances avec un algorithme non aléatoire ?
Pensez aux tournois de basket, impliquant n équipes. On forme un arbre binaire complet à n feuilles ; chaque nœud interne représente un jeu d'élimination. Ainsi, au niveau inférieur, il y a n/2 jeux, et les n/2 gagnants passent à un jeu au niveau suivant de l'arbre. En supposant que la meilleure équipe gagne toujours son match, la meilleure équipe gagne toujours tous ses matchs et peut être considérée comme le vainqueur du dernier match.
(Tout cela pourrait facilement être exprimé en pseudo-code. Jusqu'à présent, c'est juste un algorithme compliqué pour trouver un minimum ou un maximum, qui a des avantages pratiques, à savoir qu'il est parallèle (plusieurs jeux peuvent être joués à la fois) et juste (en revanche , si nous utilisions l'algorithme min ci-dessus, les équipes placées plus tôt dans L devraient jouer beaucoup plus de matchs et seraient très désavantagées).
Maintenant, où dans l'arbre pourrait être la deuxième meilleure équipe ? Cette équipe battrait toujours tout le monde sauf le vainqueur éventuel. Mais il a dû perdre une fois (puisque seul le grand vainqueur ne perd jamais). Il a donc dû perdre face au vainqueur final. Par conséquent, c'est l'une des équipes log n qui a joué le vainqueur final et nous pouvons exécuter un autre algorithme de tournoi parmi ces valeurs.
Si nous exprimons cela comme un algorithme pour trouver le deuxième meilleur, il n'utilise que des comparaisons n + plafond (log n), encore mieux que l'algorithme de cas moyen ci-dessus.
Si vous y réfléchissez, le tournoi d'élimination décrit ci-dessus est similaire à certains égards à un tas binaire. Et le processus de recherche du deuxième meilleur (en parcourant les équipes qui ont joué le vainqueur) est similaire au processus de suppression du minimum d'un tas. On peut donc utiliser des tas pour étendre l'idée à d'autres petites valeurs de k :
heapselect(L,k)
{ heap H = heapify(L) for (i = 1; i < k; i++) remove min(H) return min(H) } Le temps est évidemment O(n + k log n), donc si k = O(n/log n), le résultat est O(n). Ce qui est intéressant, mais n'aide toujours pas à trouver la médiane.
3 programmes sources C#
utiliser le système ;
espace de noms Legalsoft.Truffer
{ public class Heapselect { private int m { get; ensemble; } entier privé n { obtenir ; ensemble; } privé int srtd { obtenir ; ensemble; } tas double [] privé { obtenir ; ensemble; }
public Heapselect(int mm)
{ this.m = mm; this.n = 0 ; this.srtd = 0 ; this.heap = nouveau double[mm] ; for (int i = 0; i < mm; i++) { tas[i] = 1.0E99; } }
public void add(double val)
{ if (n < m) { tas[n++] = val; if (n == m) { Array.Sort(tas); } } sinon { si (val > tas[0]) { tas[0] = val ; pour (int j = 0; ;) { int k = (j << 1) + 1; si (k > m - 1) { pause ;
}
si (k != (m - 1) && tas[k] > tas[k + 1])
{ k++; } si (tas[j] <= tas[k]) { break; } Globals.SWAP(ref tas[k], ref tas[j]); j = k; } } n++ ; } srtd = 0 ; }
public double report(int k)
{ int mm = Math.Min(n, m); if (k > mm - 1) { throw new Exception("Heapselect k too big"); } si (k == m - 1) { retour tas[0] ; } if (srtd == 0) { Array.Sort(heap); strd = 1 ; } tas de retour[mm - 1 - k] ; } } }
4 Code de référence C
/***********************************************************************
* Author: Isai Damier
* Title: Find the Greatest k values
* Project: geekviewpoint
* Package: algorithms
*
* Statement:
* Given a list of values, find the top k values.
*
* Time Complexity: O(n log n)
*
* Sample Input: {21,3,34,5,13,8,2,55,1,19}; 4
* Sample Output: {19,21,34,55}
*
* Technical Details: This selection problem is a classic and so has
* many very good solutions. In fact, any sorting algorithm can be
* modified to solve this problem. In the worst case, the problem
* can indeed be reduced to a sorting problem: where the collection
* is first sorted and then the element at indices 0 to k-1 are
* retrieved.
*
* Presently the problem is solved using a modified version of
* heapsort called heapselect.
**********************************************************************/
public int[] heapselectTopK(int[] G, int k) {
int last = G.length - 1;
//convert array to heap in O(n)
int youngestParent = last / 2;//l = 2*p+1: p=(l-1)/2
for (int i = youngestParent; i >= 0; i--) {
moveDown(G, i, last);
}
//sort up to k (i.e. find the kth)
int limit = last - k;
for (int i = last; i > limit; i--) {
if (G[0] > G[i]) {
swap(G, 0, i);
moveDown(G, 0, i - 1);
}
}
return Arrays.copyOfRange(G, G.length - k, G.length);
}
private void moveDown(int[] A, int first, int last) {
int largest = 2 * first + 1;
while (largest <= last) {
if (largest < last && A[largest] < A[largest + 1]) {
largest++;
}
if (A[first] < A[largest]) {
swap(A, first, largest);
first = largest;
largest = 2 * first + 1;
} else {
return;
}
}
}