(一)
Main operations of a queue:
PushBack(e); PopFront().
Priority queue is a generalization of a queue where each element is assigned a priority and elements come out in order by priority.
Main operations of Priority Queue:
Insert(job): Add a job.
ExtractMax(): To process a data with the highest priority.
Remove(it): Removes an element pointer by an iteratorit.
GetMax(): Returns an element with maximum priority (without changing set of elements).
ChangePriority(it,p): changes the priority of an element pointed by it top.
Algorithms that use Priority Queue:
Dijkstra's : finding a shortest path in a graph
Prim's : constructing a minimum spanning tree of a graph
Huffman's : constructing an optimum prefix-free encoding of a string
Heap sort: sorting a given sequeue
Summary:
Insert ExtractMax
unsorted array/list O(1) O(n)
sorted array/list O(n) O(1)
Binary heap O(logn) O(logn)
(二) Binary Heap
Binary min-heap, Binary max-heap
Operations:
GetMax(): Return the root value. (Runtime: O(1))
Insert() : Attach a new node to any leaf, then sift up.
ExtractMax(): Replace the root with any leaf, then sift down. (Runtime: O(tree height))
ChangePriority(): Change a value of node, then sift up/down. (Runtime: O(tree height))
Remove(): change the priority of the element to infinity, let it sift up then extract the maximum. (Runtime: O(tree height))
Complete Binary Tree:
A binary tree is complete if all its levels arefilled except possibly the last one which isfilled from left to right.
Advantages:
Low height: at most O(logn)
Store as Array
What do we pay for these advantages?
We need to keep the tree complete.
Which binary heap operations modifythe shape of the tree?
Only Insert and ExtractMax (Remove changes the shape by calling ExtractMax).
To insert an element as a leaf in the leftmost vacant position in the last level and let it sift up.
To extract the maximum value, replace the root by the last leaf and let it sift down.
Pseudocode:
Parent(i) |
return ⌊i/2⌋ |
LeftChild(i) |
return 2i |
RightChild(i) |
return 2i + 1 |
SiftUp(i) |
while i > 1 and H[Parent(i)] < H[i]: swap H[Parent(i)] and H[i] |
SiftDown(i) |
maxIndex ← i maxIndex ← l maxIndex ← r if i ̸= maxIndex: swap H[i] and H[maxIndex] SiftDown(maxIndex ) |
Insert(p) |
if size = maxSize: return ERROR size ← size + 1 H[size] ← p SiftUp(size ) |
ExtractMax() |
result ← H[1] H[1] ← H[size] size ← size − 1 SiftDown(1) return result |
Remove(i) |
H[i] ← ∞ SiftUp(i ) ExtractMax() |
ChangePriority(i, p) |
oldp ← H[i] H[i] ← p SiftUp(i ) else: SiftDown(i ) |
Summary:
The resulting implement is:
Fast: all operations work in time O(logn)(GetMax work in O(1)).
Space efficient: store as an array of priority; parent-child connections are not stored,but are computed on the fly.
Easy to implemented: all operations are implemented in just a few lines of code.
Heap sort: Runtime: nlogn
HeapSort(A[1. . . n]) |
create an empty priority queue for i from 1 to n: Insert(A[i ]) A[i] ← ExtractMax() |
Turn an array into a heap:
BuildHeap(A[1. . . n]) |
size ← n SiftDown(i ) |
In-place Heap Sort:
HeapSort(A[1. . . n]) |
BuildHeap(A) {size = n} repeat (n − 1) times: swap A[1] and A[size] size ← size − 1 SiftDown(1) |