Hành vi có thể được sao chép bằng cách sử dụng vector 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)
Thuật toán Push
là đúng. Nó xây dựng một min-heap trong một cách đơn giản:
- Bắt đầu từ phía dưới bên phải
- Nếu giá trị lớn hơn node cha sau đó chèn nó và trở
- Nếu không, đặt thay vì phụ huynh trong vị trí dưới bên phải, sau đó thử chèn giá trị tại nơi cha mẹ (và giữ trao đổi lên cây cho đến đúng nơi đã được tìm thấy)
cây kết quả là:
0
/ \
/ \
1 2
/\ /
4 5 3
Sự cố xảy ra với phương pháp Pop
. Nó bắt đầu bằng cách xem xét nút trên cùng như là một "khoảng trống" để lấp đầy (kể từ khi chúng tôi bật nó):
*
/ \
/ \
1 2
/\ /
4 5 3
Để điền vào nó, nó tìm kiếm đứa trẻ ngay lập tức thấp nhất (trong trường hợp này: 1). sau đó nó di chuyển giá trị lên đến lấp đầy khoảng trống (và đứa trẻ bây giờ là khoảng cách mới):
1
/ \
/ \
* 2
/\ /
4 5 3
sau đó nó làm điều chính xác cùng với khoảng cách mới, vì vậy khoảng cách di chuyển xuống một lần nữa:
1
/ \
/ \
4 2
/\ /
* 5 3
Khi khoảng cách đã chạm đáy, các thuật toán ... có giá trị từ dưới tận 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 ở đáy nút ngoài cùng bên phải, nó giảm _count
để loại bỏ khoảng cách khỏi cây:
1
/ \
/ \
4 2
/\
3 5
Và chúng tôi kết thúc bằng ... Một đống bị hỏng.
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 hầu hết, tôi có thể trao đổi nó với một phiên bản làm việc (không biết xấu hổ sao chép 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 mã số đó là việc thực hiện đệ quy, mà sẽ phá vỡ nếu số phần tử quá lớn. Tôi 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ĩ tôi đã phát hiện ra những gì còn thiếu. Sau khi lấy nút dưới cùng bên phải, tác giả chỉ quên cân bằng lại đống:
internal void Pop()
{
Debug.Assert(_count != 0);
if (_count > 1)
{
// Loop invariants:
//
// 1. parent is the index of a gap in the logical tree
// 2. leftChild is
// (a) the index of parent's left child if it has one, or
// (b) a value >= _count if parent is a leaf node
//
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;
// Promote bestChild to fill the gap left by parent.
_heap[parent] = _heap[bestChild];
// Restore invariants, i.e., let parent point to the gap.
parent = bestChild;
leftChild = HeapLeftChild(parent);
}
// Fill the last gap by moving the last (i.e., bottom-rightmost) node.
_heap[parent] = _heap[_count - 1];
// FIX: Rebalance the heap
int index = parent;
var value = _heap[parent];
while (index > 0)
{
int parentIndex = HeapParent(index);
if (_comparer.Compare(value, _heap[parentIndex]) < 0)
{
// value is a better match than the parent node so exchange
// places to preserve the "heap" property.
var pivot = _heap[index];
_heap[index] = _heap[parentIndex];
_heap[parentIndex] = pivot;
index = parentIndex;
}
else
{
// Heap is balanced
break;
}
}
}
_count--;
}
Theo nhận xét trong mã nguồn, Microsoft đã sử dụng mã này từ 2005-02-14. Tôi tự hỏi làm thế nào một lỗi như thông báo thoát này trong hơn 12 năm? – Nat
@Nat vì nơi duy nhất mà microsoft sử dụng nó [ở đây] (https://referencesource.microsoft.com/#PresentationCore/Core/CSharp/MS/Internal/FontFace/PhysicalFontFamily.cs,185) và một phông chữ chọn thấp hơn kiểu chữ ưu tiên một số thời gian là một lỗi khó nhận thấy. –