Поведінку можна відтворити, використовуючи вектор ініціалізації [0, 1, 2, 4, 5, 3]
. Результат:
[0, 1, 2, 4, 3, 5]
(ми бачимо, що 3 неправильно розміщено)
Push
Алгоритм є правильним. Він створює міні-кучу просто:
- Почніть знизу праворуч
- Якщо значення більше батьківського вузла, вставте його та поверніть
- В іншому випадку поставте замість батьків у нижнє праве положення, а потім спробуйте вставити значення в батьківське місце (і продовжуйте міняти дерево, поки не знайдете потрібне місце)
Отримане дерево:
0
/ \
/ \
1 2
/ \ /
4 5 3
Питання в Pop
методі. Починається з того, що верхній вузол розглядається як "прогалина", яку потрібно заповнити (оскільки ми її вивели):
*
/ \
/ \
1 2
/ \ /
4 5 3
Щоб заповнити його, він шукає найменшу безпосередню дитину (у даному випадку: 1). Потім воно переміщує значення вгору, щоб заповнити пробіл (і дочірній статус тепер є новим пробілом):
1
/ \
/ \
* 2
/ \ /
4 5 3
Потім він робить те саме те саме з новим розривом, тому розрив знову рухається вниз:
1
/ \
/ \
4 2
/ \ /
* 5 3
Коли пробіл досягне дна, алгоритм ... приймає крайнє нижнє значення дерева і використовує його для заповнення прогалини:
1
/ \
/ \
4 2
/ \ /
3 5 *
Тепер, коли розрив знаходиться в самому нижньому правому вузлі, він зменшується, _count
щоб видалити розрив з дерева:
1
/ \
/ \
4 2
/ \
3 5
І у нас закінчується ... Розбита купа.
Чесно кажучи, я не розумію, що намагався зробити автор, тому я не можу виправити існуючий код. Щонайбільше, я можу поміняти його робочою версією (безсоромно скопійованою з Вікіпедії ):
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);
}
}
Основною проблемою цього коду є рекурсивна реалізація, яка зламається, якщо кількість елементів занадто велика. Настійно рекомендую замість цього використовувати оптимізовану бібліотеку третьої сторони.
Редагувати: Здається, я з’ясував, чого не вистачає. Взявши крайній правий вузол, автор просто забув збалансувати купу:
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--;
}