web-dev-qa-db-fra.com

Comment supprimer dans une structure de données de tas?

Je comprends comment supprimer le nœud racine d'un tas max, mais la procédure de suppression d'un nœud du milieu consiste-t-elle à supprimer et à remplacer la racine à plusieurs reprises jusqu'à ce que le nœud souhaité soit supprimé?

  1. O (log n) est-il la complexité optimale pour cette procédure?

  2. Cela affecte-t-il la complexité du grand O, car d'autres nœuds doivent être supprimés pour supprimer un nœud spécifique?

44
tesfa koli

En fait, vous pouvez supprimer un élément du milieu d'un tas sans problème.

L'idée est de prendre le dernier élément du tas et, à partir de la position actuelle (c'est-à-dire la position qui contenait l'élément que vous avez supprimé), de le tamiser si le nouvel élément est supérieur au parent de l'ancien élément. S'il n'est pas supérieur au parent, tamisez-le.

C'est la procédure pour un tas max. Pour un tas min, bien sûr, vous inverseriez les cas plus grands et moins.

Trouver un élément dans un tas est une opération O(n), mais si vous savez déjà où il se trouve) le tas, le retirer est O (log n).

J'ai publié une file d'attente prioritaire basée sur le tas pour DevSource il y a quelques années. Voir ne implémentation de file d'attente prioritaire en C # . Il a une méthode RemoveAt qui fait exactement ce que j'ai décrit.

La source complète est à http://www.mischel.com/pubs/priqueue.Zip

Mise à jour

Plusieurs ont demandé s'il était possible de remonter après avoir déplacé le dernier nœud du tas pour remplacer le nœud supprimé. Considérez ce tas:

        1
    6       2
  7   8   3

Si vous supprimez le nœud avec la valeur 7, la valeur 3 le remplace:

        1
    6       2
  3   8

Vous devez maintenant le déplacer vers le haut pour créer un tas valide:

        1
    3       2
  6   8

La clé ici est que si l'élément que vous remplacez se trouve dans un sous-arbre différent du dernier élément du tas, il est possible que le nœud de remplacement soit plus petit que le parent du nœud remplacé.

83
Jim Mischel

Le problème avec la suppression d'un élément arbitraire d'un tas est que vous ne le trouvez pas.

Dans un tas, la recherche d'un élément arbitraire est O(n), donc supprimer un élément [s'il est donné par une valeur] est également O(n).

S'il est important pour vous de supprimer des éléments arbitraires de la structure de données, un tas n'est probablement pas le meilleur choix, vous devriez plutôt envisager des structures de données triées telles que BST équilibré ou a ignorer liste .

Si votre élément est donné par référence, il est cependant possible de le supprimer dans O(logn) en le 'remplaçant' simplement par la dernière feuille [rappelez-vous qu'un tas est implémenté comme un arbre binaire complet, donc il y a un dernier feuille, et vous savez exactement où il se trouve], supprimez ces éléments et re-tassez le sous-tas correspondant.

16
amit

Si vous avez un tas max, vous pouvez l'implémenter en attribuant une valeur plus grande que toute autre (par exemple quelque chose comme int.MaxValue ou inf dans la langue que vous utilisez) possible pour l'élément à supprimer, puis re-heapify et ce sera la nouvelle racine. Effectuez ensuite une suppression régulière du nœud racine.

Cela entraînera une nouvelle re-heapify, mais je ne vois pas de moyen évident d'éviter de le faire deux fois. Cela suggère qu'un tas ne convient peut-être pas à votre cas d'utilisation, si vous devez souvent extraire des nœuds du milieu.

(pour un segment min, vous pouvez évidemment utiliser int.MinValue ou -inf ou peu importe)

2
rejj

Ce que vous voulez réaliser n'est pas une opération de tas typique et il me semble qu'une fois que vous introduisez "supprimer l'élément central" comme méthode, un autre arbre binaire (par exemple rouge-noir ou arbre AVL) est un meilleur choix. Vous avez un arbre rouge-noir implémenté dans certaines langues (par exemple, la carte et défini en c ++).

Sinon, la façon de supprimer un élément central est celle proposée dans la réponse de rejj: attribuez une grande valeur (pour le tas max) ou une petite valeur (pour le tas min) à l'élément, tamisez-le jusqu'à ce qu'il soit root, puis supprimez-le.

Cette approche conserve toujours la complexité O(log(n)) pour la suppression des éléments intermédiaires, mais celle que vous proposez le fait. Elle aura la complexité O (n * log (n)) et est donc pas très bon. J'espère que ça aide.

1
Ivaylo Strandjev