Das Verhalten kann unter Verwendung des Initialisierungsvektors reproduziert werden [0, 1, 2, 4, 5, 3]
. Das Ergebnis ist:
[0, 1, 2, 4, 3, 5]
(Wir können sehen, dass 3 falsch platziert ist)
Der Push
Algorithmus ist korrekt. Es baut auf einfache Weise einen Min-Haufen auf:
- Beginnen Sie unten rechts
- Wenn der Wert größer als der übergeordnete Knoten ist, fügen Sie ihn ein und geben Sie ihn zurück
- Andernfalls setzen Sie stattdessen das übergeordnete Element an die untere rechte Position und versuchen Sie dann, den Wert an der übergeordneten Stelle einzufügen (und tauschen Sie den Baum weiter aus, bis die richtige Stelle gefunden wurde).
Der resultierende Baum ist:
0
/ \
/ \
1 2
/ \ /
4 5 3
Das Problem ist mit der Pop
Methode. Es beginnt damit, dass der oberste Knoten als eine "Lücke" betrachtet wird, die gefüllt werden muss (da wir ihn geöffnet haben):
*
/ \
/ \
1 2
/ \ /
4 5 3
Um es zu füllen, sucht es nach dem niedrigsten unmittelbaren Kind (in diesem Fall: 1). Anschließend wird der Wert nach oben verschoben, um die Lücke zu füllen (und das Kind ist jetzt die neue Lücke):
1
/ \
/ \
* 2
/ \ /
4 5 3
Es macht dann genau das Gleiche mit der neuen Lücke, sodass sich die Lücke wieder nach unten bewegt:
1
/ \
/ \
4 2
/ \ /
* 5 3
Wenn die Lücke den Boden erreicht hat, nimmt der Algorithmus ... den Wert ganz rechts unten des Baums und füllt damit die Lücke:
1
/ \
/ \
4 2
/ \ /
3 5 *
Nachdem sich die Lücke am Knoten ganz rechts unten befindet, wird sie dekrementiert _count
, um die Lücke aus dem Baum zu entfernen:
1
/ \
/ \
4 2
/ \
3 5
Und am Ende haben wir ... einen kaputten Haufen.
Um ganz ehrlich zu sein, verstehe ich nicht, was der Autor versucht hat, daher kann ich den vorhandenen Code nicht reparieren. Ich kann es höchstens gegen eine Arbeitsversion austauschen (schamlos aus Wikipedia kopiert ):
internal void Pop2()
{
if (_count > 0)
{
_count--;
_heap[0] = _heap[_count];
Heapify(0);
}
}
internal void Heapify(int i)
{
int left = (2 * i) + 1;
int right = left + 1;
int smallest = i;
if (left <= _count && _comparer.Compare(_heap[left], _heap[smallest]) < 0)
{
smallest = left;
}
if (right <= _count && _comparer.Compare(_heap[right], _heap[smallest]) < 0)
{
smallest = right;
}
if (smallest != i)
{
var pivot = _heap[i];
_heap[i] = _heap[smallest];
_heap[smallest] = pivot;
Heapify(smallest);
}
}
Das Hauptproblem bei diesem Code ist die rekursive Implementierung, die unterbrochen wird, wenn die Anzahl der Elemente zu groß ist. Ich empfehle dringend, stattdessen eine optimierte Drittanbieter-Bibliothek zu verwenden.
Edit: Ich glaube ich habe herausgefunden was fehlt. Nachdem der Autor den Knoten ganz rechts unten genommen hatte, vergaß er nur, den Heap neu auszugleichen:
internal void Pop()
{
Debug.Assert(_count != 0);
if (_count > 1)
{
int parent = 0;
int leftChild = HeapLeftChild(parent);
while (leftChild < _count)
{
int rightChild = HeapRightFromLeft(leftChild);
int bestChild =
(rightChild < _count && _comparer.Compare(_heap[rightChild], _heap[leftChild]) < 0) ?
rightChild : leftChild;
_heap[parent] = _heap[bestChild];
parent = bestChild;
leftChild = HeapLeftChild(parent);
}
_heap[parent] = _heap[_count - 1];
int index = parent;
var value = _heap[parent];
while (index > 0)
{
int parentIndex = HeapParent(index);
if (_comparer.Compare(value, _heap[parentIndex]) < 0)
{
var pivot = _heap[index];
_heap[index] = _heap[parentIndex];
_heap[parentIndex] = pivot;
index = parentIndex;
}
else
{
break;
}
}
}
_count--;
}