El comportamiento se puede reproducir utilizando el vector de inicialización [0, 1, 2, 4, 5, 3]
. El resultado es:
[0, 1, 2, 4, 3, 5]
(podemos ver que 3 está colocado incorrectamente)
El Push
algoritmo es correcto. Construye un min-heap de una manera sencilla:
- Empiece desde abajo a la derecha
- Si el valor es mayor que el nodo principal, insértelo y regrese
- De lo contrario, coloque el padre en la posición inferior derecha, luego intente insertar el valor en el lugar del padre (y siga intercambiando el árbol hasta que se encuentre el lugar correcto)
El árbol resultante es:
0
/ \
/ \
1 2
/ \ /
4 5 3
El problema está en el Pop
método. Comienza considerando el nodo superior como un "espacio" para llenar (ya que lo abrimos):
*
/ \
/ \
1 2
/ \ /
4 5 3
Para llenarlo, busca el hijo inmediato más bajo (en este caso: 1). Luego mueve el valor hacia arriba para llenar el espacio (y el niño ahora es el nuevo espacio):
1
/ \
/ \
* 2
/ \ /
4 5 3
Luego hace exactamente lo mismo con el nuevo espacio, por lo que el espacio se mueve hacia abajo nuevamente:
1
/ \
/ \
4 2
/ \ /
* 5 3
Cuando el espacio ha llegado al final, el algoritmo ... toma el valor del extremo inferior derecho del árbol y lo usa para llenar el espacio:
1
/ \
/ \
4 2
/ \ /
3 5 *
Ahora que la brecha está en el nodo de la parte inferior derecha, disminuye _count
para eliminar la brecha del árbol:
1
/ \
/ \
4 2
/ \
3 5
Y terminamos con ... Un montón roto.
Para ser perfectamente honesto, no entiendo qué estaba tratando de hacer el autor, así que no puedo arreglar el código existente. A lo sumo, puedo cambiarlo con una versión funcional (copiado descaradamente de Wikipedia ):
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);
}
}
El problema principal con ese código es la implementación recursiva, que se romperá si el número de elementos es demasiado grande. En su lugar, recomiendo encarecidamente utilizar una biblioteca de terceros optimizada.
Editar: Creo que descubrí lo que falta. Después de tomar el nodo de la parte inferior derecha, el autor simplemente se olvidó de reequilibrar el montón:
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--;
}