Computer Algorithms CISC4080 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) The # of swaps is at most the height 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) HeapifyDown (largest) // 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
Suppose node 10 gets a new value, 100? 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) 100 1)Swap (A[10],A[5]) ===> 100 7 || 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) 14 14 2 HeapifyDown (3): ===> HeapifyDown (2): two swaps to sink 1 to leaf <==== 14 14 2
BuildHeap (cont’d) HeapifyDown (1): 14 14
BuildHeap() BuildHeap() HeapifyDown (i) 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
HeapSort Demo HeapifyDown (1)
HeapSort demo HeapifyDown (1)
Continues until the heap is empty … 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)