250 likes | 283 Views
Learn how to efficiently implement a priority queue using a heap data structure, and discover the heapsort algorithm for sorting. This course covers priority queue operations such as insertion, extraction, and key modification.
E N D
Computer AlgorithmsCISC4080 CIS, Fordham Univ. Instructor: X. Zhang
Outline • Priority queues • Heap: as a data structure implementing priority queue efficiently • heapsort: sorting algorithm that use heap data structure
Priority Queue • A data structure storing a set S of elements (where each element has a key field) supporting the following operations: • S.insert(x): insert element x into the priority queue • S.max(): return element from S with largest key value • S.extract_max(): return and remove element with largest key • S.increase_key (x, k): increase element x’s key to k • S.decrease_key (x, k): decrease element x’s key to k
Implementing Priority Queue • There are many options: • unsorted array: linear time to extract max, constant time to insert, and increase/decrease key • sorted array: constant time to extract max, linear time to insert, and increase/decrease key • heap: log n time to insert, increase/decrease key value, constant time to extract max
Heap • Heap: is used to implement a priority queue, and to implement heapsort • A heap is an array/vector visualized as a complete binary tree; or equivalently, a complete binary tree stored in an array/vector, in which every node satisfies (max) heap property is satisfied. • Max Heap Property: the key of a node is >= than the keys of its children
Representing CBT in array • Complete Binary Tree: binary tree in which all levels (with possible exception of lowest level) are full, and lowest level nodes are filled from left to right. • can be stored in an array (no pointers required)!
Heap Operations • BuildHeap: produce a (max) heap from an array • Note: sorting array in descending order to build a heap takes time n logn. • We will do it in linear time • insert (x): insert element x into heap • extract_max(): return and remove largest element from heap • modify (i, k): modify the key of i-th element to k • When implementing above operations, we use following helper functions: • heapfiy_down (i): correct a single violation for subtree at its root i (sink a small value down the tree) • heapfiy_up (i): float a large value at i-th node up the tree
heapify_down(i) • (A local operation) Consider node i • it’s left sub-tree (with left(i) as its root) is heap • it’s right sub-tree (with right(i) as its root) is heap • but node i violates heap property ( • i.e., A[i] < A[left(i)] or A[i] < A[right(i)] ) • heapfiy_down (i) trickles A[i] down the tree, make the sub-tree rooted at node i a heap
HeapifyDown (i) • HeapifyDown (2) • repair heap property for sub-tree • rooted at node 2 • Note: left and right sub-trees • satisfy heap property
HeapifyDown(2) • 1) Swap A[2] with A[4] • 2) Swap A[4] with A[9] • The # of swaps is at most the height • of the tree, so T(n)=O(logn) • Done! • node 9 is leaf.
HeapifyDown(i) • // if node i is not the largest, swap it down. • HeapifyDown (largest) • //since node largest gets a new/larger value, we need to repair it
HeapifyDown(i): without recursion • While (i is not a leaf node) // i<=heap_size(A)/2 • // if node i is not the largest, swap it down. • i = largest • //repeat with i is now largest
heapify_up(i) • (A local operation) Consider node i • its value is larger than its parent, and maybe other ancestors • heapfiy_up (i) float value A[i] up the tree, so that its parent value is larger than it • Suppose node 10 gets a new value, 100? • 100
heapify_up(10) • 1)Swap (A[10],A[5]) • ===> • 100 • 7 • 100 • 2) Swap (A[5],A[2]) • || • V • 100 • 3)Swap (A[3],A[1]) • <== • 16 • 100 • 14 • 14 • 7 • 7
HeapifyUp (i): recursive • if i==1 return; //already at root • p = parent (i) • if A[p]<A[i] • swap (A[i], A[p]) • HeapifyUp (p) //parent gets a larger value…
BuildHeap • Idea: use heapifyDown to build small heaps first (a bottom up approach) • In the beginning, we have small heaps (leaf nodes, node 6, node 7, … node 10) • HeapifyDown (5): • make sub-tree rooted at 5 a heap • HeapifyDown (4): … repair subtree rooted at node 4 • 14 • 2
BuildHeap (cont’d) • HeapifyDown (3): • ===> • HeapifyDown (2): • two swaps to sink 1 to leaf • <==== • 14 • 14 • 2
BuildHeap (cont’d) • HeapifyDown (1): • 14 • 14
BuildHeap() BuildHeap() for i=HeapLength()/2 to 1 HeapifyDown (i) • Start from the last non-leaf node (n/2), work our way up to the root, calling HeapifyDown to repair/build small heaps, and using them to build larger heap. • Running time is T(n)=O(n) (derivation omitted)
Heap Operations: how • BuildHeap: produce a (max) heap from an array • insert (x): insert element x into heap • add element to the end of array • HeapifyUp on the new node • extract_max(): return and remove largest element from heap • store root element • swap last element with root • HeapifyDown (root=1) • modify (i, k): modify the key of i-th element to k • To a larger value: HeapifyUp (i) • To a smaller value: HeapifyDown (i)
HeapSort Demo • HeapifyDown (1)
HeapSort demo • HeapifyDown (1)
HeapSort Demo • HeapifyDown (1) • Continues until the heap is empty …
HeapSort running time • The loop iterates for n times • Each iteration involves a swap and HeapifyDown ==> O (log n) • So T(n) = O (n log n)