Hành vi có thể được tái tạo bằng cách sử dụng vectơ khởi tạo [0, 1, 2, 4, 5, 3]
. Kết quả là:
[0, 1, 2, 4, 3, 5]
(chúng ta có thể thấy rằng 3 được đặt không chính xác)
Các Push
thuật toán là đúng. Nó xây dựng min-heap một cách đơn giản:
- Bắt đầu từ dưới cùng bên phải
- Nếu giá trị lớn hơn nút cha thì chèn nó và trả về
- Nếu không, hãy đặt cây gốc ở vị trí dưới cùng bên phải, sau đó thử chèn giá trị ở vị trí gốc (và tiếp tục hoán đổi cây cho đến khi tìm thấy đúng vị trí)
Cây kết quả là:
0
/ \
/ \
1 2
/ \ /
4 5 3
Vấn đề là với Pop
phương pháp. Nó bắt đầu bằng cách coi nút trên cùng là "khoảng trống" cần lấp đầy (vì chúng tôi đã đưa nó vào):
*
/ \
/ \
1 2
/ \ /
4 5 3
Để điền vào nó, nó tìm kiếm con thấp nhất ngay lập tức (trong trường hợp này là: 1). Sau đó, nó di chuyển giá trị lên để lấp đầy khoảng trống (và con bây giờ là khoảng trống mới):
1
/ \
/ \
* 2
/ \ /
4 5 3
Sau đó, nó thực hiện điều tương tự với khoảng cách mới, vì vậy khoảng cách lại di chuyển xuống:
1
/ \
/ \
4 2
/ \ /
* 5 3
Khi khoảng trống đã chạm đến đáy, thuật toán ... lấy giá trị dưới cùng bên phải của cây và sử dụng nó để lấp đầy khoảng trống:
1
/ \
/ \
4 2
/ \ /
3 5 *
Bây giờ khoảng cách nằm ở nút ngoài cùng bên phải, nó giảm _count
dần để loại bỏ khoảng cách khỏi cây:
1
/ \
/ \
4 2
/ \
3 5
Và chúng ta kết thúc với ... Một đống đổ vỡ.
Thành thật mà nói, tôi không hiểu tác giả đang cố gắng làm gì, vì vậy tôi không thể sửa mã hiện có. Tối đa, tôi có thể hoán đổi nó với một phiên bản đang hoạt động (sao chép một cách đáng xấu hổ từ 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);
}
}
Vấn đề chính với đoạn mã đó là việc triển khai đệ quy, sẽ bị hỏng nếu số lượng phần tử quá lớn. Tôi thực sự khuyên bạn nên sử dụng thư viện của bên thứ ba được tối ưu hóa để thay thế.
Chỉnh sửa: Tôi nghĩ rằng tôi đã tìm ra những gì còn thiếu. Sau khi sử dụng nút dưới cùng bên phải, tác giả đã quên cân bằng lại đống:
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--;
}