web-dev-qa-db-fra.com

Comment mettre en œuvre O(logn) opération avec touche de diminution pour la file d’attente prioritaire basée sur min-tas?

Je travaille sur une application qui illustre le l'algorithme de Djikstra, et pour l'utiliser, je dois restaurer la propriété heap lorsque la valeur de mes éléments est réduite.

Le problème concernant la complexité est que lorsque l'algorithme change la valeur d'un élément, l'index de cet élément dans la structure interne (heap dans ce cas) utilisé pour la file d'attente prioritaire est inconnu. En tant que tel, je dois actuellement effectuer une recherche O(n), afin de récupérer l'index, avant de pouvoir effectuer une touche de diminution réelle.

De plus, je ne suis pas tout à fait sûr du code nécessaire à l'opération. J'utilise le D-Heap ici pour ma file d'attente prioritaire. Un pseudocode aiderait, mais je préférerais un exemple en Java sur la manière de procéder.

42
jathanasiou

Vous pouvez effectuer les opérations suivantes: stocker dans votre segment de mémoire une table de hachage qui mappe votre segment de mémoire values ​​ en index de segment de mémoire. Ensuite, vous devriez étendre un peu votre logique de tas habituelle:

on Swap(i, j): 
    map[value[i]] = j; 
    map[value[j]] = i;

on Insert(key, value): 
    map.Add(value, heapSize) in the beginning;

on ExtractMin: 
    map.Remove(extractedValue) in the end;

on UpdateKey(value, newKey): 
    index = map[value]; 
    keys[index] = newKey; 

BubbleUp(index) en cas de DecreaseKey, et BubbleDown/Heapify(index) en cas de IncreaseKey, pour restaurer min-heap-property.

Voici ma mise en œuvre C #: http://Pastebin.com/kkZn123m

Insérez et ExtractMin appelez Swap log (N) times, lors de la restauration de la propriété de segment de mémoire. Et vous ajoutez O(1) la surcharge au swap, de sorte que les deux opérations restent O (log (n)). UpdateKey est également log (N): vous commencez par rechercher l'index dans une table de hachage pour O (1), puis vous restaurez la propriété de segment de mémoire pour O(log(N)) comme vous le faites dans Insert/ExtractMin.

Remarque importante: l'utilisation de valeurs pour la recherche d'index nécessitera qu'elles soient UNIQUES. Si cette condition ne vous convient pas, vous devrez ajouter un identifiant de file d'attente unique à vos paires clé-valeur et conserver le mappage entre cet identifiant de file d'attente unique et l'index de segment de mémoire au lieu du mappage index-valeur. Mais pour Dijkstra, cela n’est pas nécessaire, car vos valeurs seront des nœuds de graphe et vous ne voudrez pas de nœuds en double dans votre file d’attente prioritaire.

27
ptkvsk

Per cette SO question il n’est pas nécessaire d’avoir une méthode de clé de diminution pour implémenter l’algorithme de Dijkstra.

Vous pouvez simplement ajouter un élément à la file d'attente prioritaire autant de fois que nécessaire et garder trace des nœuds que vous avez visités pour éliminer les doublons. La première fois que vous visitez réellement un nœud en le retirant de la file d'attente, vous avez trouvé le chemin le plus court vers ce nœud et pouvez en ignorer toutes les occurrences futures dans la file d'attente prioritaire.

Avoir beaucoup de nœuds supplémentaires dans la file d'attente prioritaire n'est pas un problème car il s'agit d'une structure O(log N). (Vous devez effectuer environ 20 comparaisons pour 1 million d'éléments et 30 comparaisons pour 1 milliard d'éléments.)

Edit: Pour faire suite à cette question plus tard, ma réponse me déçoit un peu: toutes ces choses devront sortir de la file d'attente à moins que vous ne fassiez quelques canipulations spéciales plus tard. Comme beaucoup de choses dans la vie, cela dépend de la façon dont vous gérez votre mémoire et des coûts associés. Mais le point général demeure: la touche de diminution n'est pas nécessaire même si cela peut être souhaitable.

16
Richard

Si vous utilisez c ++ stl make_heap ()/pop_heap ()/Push_heap (), il n’existe aucun moyen de conserver un index de noeud id à index dans le vecteur de soulignement, je pense que vous devez implémenter vos propres fonctions de tas pour atteindre O(logn) en mode Augmenter/Réduire.

0
zach