C++ Classes and Data Structures Jeffrey S. Childs

Slides:



Advertisements
Similar presentations
1 A full binary tree A full binary tree is a binary tree in which all the leaves are on the same level and every non leaf node has two children. SHAPE.
Advertisements

1 A Two-Level Binary Expression ‘-’ ‘8’ ‘5’ treePtr INORDER TRAVERSAL : has value 3 PREORDER TRAVERSAL: POSTORDER TRAVERSAL: 8 5 -
Heapsort.
Heapsort. 2 Why study Heapsort? It is a well-known, traditional sorting algorithm you will be expected to know Heapsort is always O(n log n) Quicksort.
Data Structures Using C++ 2E Chapter 11 Binary Trees and B-Trees.
Heapsort Based off slides by: David Matuszek
1 HEAPS & PRIORITY QUEUES Array and Tree implementations.
Tree.
Compiled by: Dr. Mohammad Alhawarat BST, Priority Queue, Heaps - Heapsort CHAPTER 07.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 8 Stacks and Queues Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.
C++ Classes and Data Structures Jeffrey S. Childs
CS 1031 Tree Traversal Techniques; Heaps Tree Traversal Concept Tree Traversal Techniques: Preorder, Inorder, Postorder Full Trees Almost Complete Trees.
Heapsort CSC Why study Heapsort? It is a well-known, traditional sorting algorithm you will be expected to know Heapsort is always O(n log n)
Chapter 9 Priority Queues, Heaps, Graphs, and Sets.
BINARY SEARCH TREE. Binary Trees A binary tree is a tree in which no node can have more than two children. In this case we can keep direct links to the.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 14 Introduction to Sorting Algorithms Jeffrey S. Childs Clarion University of PA © 2008, Prentice.
1 Chapter 7 Stacks and Queues. 2 Stack ADT Recall that ADT is abstract data type, a set of data and a set of operations that act upon the data. In a stack,
1 Chapter 10 Trees. 2 Definition of Tree A tree is a set of linked nodes, such that there is one and only one path from a unique node (called the root.
Priority Queue. Priority Queues Queue (FIFO). Priority queue. Deletion from a priority queue is determined by the element priority. Two kinds of priority.
CPSC 252 Binary Heaps Page 1 Binary Heaps A complete binary tree is a binary tree that satisfies the following properties: - every level, except possibly.
Heapsort. What is a “heap”? Definitions of heap: 1.A large area of memory from which the programmer can allocate blocks as needed, and deallocate them.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 11 Hash Tables Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 15 Other Data Structures Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.
HEAPS. Review: what are the requirements of the abstract data type: priority queue? Quick removal of item with highest priority (highest or lowest key.
AVL Trees and Heaps. AVL Trees So far balancing the tree was done globally Basically every node was involved in the balance operation Tree balancing can.
1 Chapter 6 Methods for Making Data Structures. 2 Dynamic Arrays in Data Structures In almost every data structure, we want functions for inserting and.
Chapter 9 Heaps and Priority Queues Lecture 18. Full Binary Tree Every non-leaf node has two children Leaves are on the same level Full Binary Tree.
Course: Programming II - Abstract Data Types HeapsSlide Number 1 The ADT Heap So far we have seen the following sorting types : 1) Linked List sort by.
Priority Queues and Heaps. John Edgar  Define the ADT priority queue  Define the partially ordered property  Define a heap  Implement a heap using.
Priority Queues and Heaps Tom Przybylinski. Maps ● We have (key,value) pairs, called entries ● We want to store and find/remove arbitrary entries (random.
Chapter 16: Linked Lists.
C++ Classes and Data Structures Jeffrey S. Childs
Binary Search Trees One of the tree applications in Chapter 10 is binary search trees. In Chapter 10, binary search trees are used to implement bags.
Priority Queues and Heaps
B+-Trees.
Hashing Exercises.
Heap Sort Example Qamar Abbas.
CMSC 341 Lecture 13 Leftist Heaps
Heap Chapter 9 Objectives Upon completion you will be able to:
ADT Heap data structure
Map interface Empty() - return true if the map is empty; else return false Size() - return the number of elements in the map Find(key) - if there is an.
Phil Tayco Slide version 1.0 May 7, 2018
CMSC 341 Lecture 10 B-Trees Based on slides from Dr. Katherine Gibson.
Chapter 9 Priority Queues, Heaps, Graphs, and Sets
C++ Classes and Data Structures Jeffrey S. Childs
Chapter 8 – Binary Search Tree
Dr. David Matuszek Heapsort Dr. David Matuszek
Heaps, Heapsort, and Priority Queues
i206: Lecture 14: Heaps, Graphs intro.
Binary Search Trees One of the tree applications in Chapter 10 is binary search trees. In Chapter 10, binary search trees are used to implement bags.
B- Trees D. Frey with apologies to Tom Anastasio
Heaps Chapter 10 has several programming projects, including a project that uses heaps. This presentation shows you what a heap is, and demonstrates two.
CS Data Structures Chapter 17 Heaps Mehmet H Gunes
B- Trees D. Frey with apologies to Tom Anastasio
Heaps Chapter 10 has several programming projects, including a project that uses heaps. This presentation shows you what a heap is, and demonstrates two.
Heaps Chapter 11 has several programming projects, including a project that uses heaps. This presentation shows you what a heap is, and demonstrates.
A Robust Data Structure
Binary Heaps What if we’re mostly concerned with finding the most relevant data? A binary heap is a binary tree (2 or fewer subtrees for each node) A heap.
Heapsort.
B- Trees D. Frey with apologies to Tom Anastasio
Heaps Chapter 11 has several programming projects, including a project that uses heaps. This presentation shows you what a heap is, and demonstrates.
Priority Queues & Heaps
Heapsort.
Heapsort.
Data Structures and Algorithm Analysis Priority Queues (Heaps)
Instructor: Dr. Michael Geiger Spring 2017 Lecture 30: Sorting & heaps
Heapsort.
CO 303 Algorithm Analysis and Design
Heaps Chapter 10 has several programming projects, including a project that uses heaps. This presentation shows you what a heap is, and demonstrates two.
Heaps.
Presentation transcript:

C++ Classes and Data Structures Jeffrey S. Childs Chapter 12 Priority Queues, Trees, and Heaps Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall

Priority Queue ADT The data in a priority queue is (conceptually) a queue of elements The “queue” can be thought of as sorted with the largest in front, and the smallest at the end Its physical form, however, may differ from this conceptual view considerably

Priority Queue ADT Operations enqueue, an operation to add an element to the queue dequeue, an operation to take the largest element from the queue an operation to determine whether or not the queue is empty an operation to empty out the queue

Another Priority Queue ADT A priority queue might also be designed to dequeue the element with the minimum value Priority queues are generally not designed to arbitrarily dequeue both minimum and maximum values, whichever the client wants at any particular time We will only consider priority queues that dequeue maximum values (can be easily modified for dequeuing minimum values)

Objects as Elements Elements can be objects in priority queues – for example, Employee objects One would overload relational operators in the Employee struct to determine what data member is being considered for maximum value The client may be interested in dequeuing the employee with the maximum age each time Or, the overloaded operator can be written to dequeue the employee with the maximum salary each time

Priority Queue Implementation To implement a priority queue, an array sorted in descending order comes to mind Dequeuing from a sorted array is easy – just get the value at the current front and increment a front index – this is ( 1 ) time However, enqueuing into a sorted array would take some time – the element would have to be inserted into its proper position in the array…

Enqueuing an Element … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 In this array of elements, each element might be an object, but only the data member considered for maximum value is shown.

Enqueuing an Element (cont.) … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 Suppose that element 71 needs to be enqueued.

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 Suppose that element 71 needs to be enqueued.

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 Suppose that element 71 needs to be enqueued. We could enqueue 71 by using a loop.

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 7 5 0 1 2 48 49 50 51 52 98 99 100 i This process continues and i eventually becomes 51 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i This process continues and i eventually becomes 51 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i FALSE for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i Now we can use: arr[ i ] = item; to enqueue the element

Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 71 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i Now we can use: arr[ i ] = item; to enqueue the element

Enqueuing an Element (cont.) If we assume that, on average, half the elements in an array need to be shifted to insert an element, then the enqueue for an array is a ( n ) algorithm In summary, when using an array for a priority queue: dequeue is ( 1 ) enqueue (on average) is ( n )

Using a Heap to Implement a Priority Queue An alternative to using a sorted array for a priority queue is to use a heap Here, heap does not mean an area of memory used for dynamic allocation – rather, it is a data structure Enqueue in a heap is a O( lg n ) operation Dequeue in a heap is a O( lg n ) operation

Comparing Operations So which is better, the heap or the array? We often eliminate a data structure that has a high time complexity in a commonly used operation, even if the other operations have very low time complexities In the array, on average, an enqueue-dequeue pair of operations takes ( n ) + ( 1 ) time, but ( 1 ) is absorbed into ( n ), leaving us with an overall time complexity of ( n ) per pair of operations

Comparing Operations (cont.) In the heap, each enqueue-dequeue pair of operations takes O( lg n ) + O( lg n ) time, giving us an overall time complexity of O( lg n ) per pair of operations The heap is usually better, although the array can be good in situations where a group of initial elements are supplied and sorted, then only dequeue operations are performed (no enqueue operations)

Trees A heap is a type of tree In order to understand heaps, we need to discuss trees first We will first limit the discussion to the linked list implementation of trees Trees can be defined with paths A path exists from node A to node B if one can follow a chain of pointers to travel from node A to node B

Paths D F A G E B C A set of linked nodes

Paths (cont.) D F A G E B C There is one path from A to B

Paths (cont.) D F A G E B C There is no path from C to B

Paths (cont.) D F A G E B C There is a path from D to B

Paths (cont.) D F A G E B There is also a second path from D to B. C

Paths (cont.) D F A G E B There is also a second path from D to B. C

Definition of Tree A tree is a set of linked nodes, such that there is one and only one path from a unique node (called the root node) to every other node in the tree

Example of a Tree root A C B D G E F

Cycles There is no cycle (circle of pointers) in a tree Any linked structure that has a cycle would have more than one path from the root node to another node

Example of a Cycle D A B C E

Tree Cannot Have a Cycle D A B Node A cannot be a root node of a tree, because there is more than one path from A to C C E

Tree Cannot Have a Cycle (cont.) D A B C One path from A to C E

Tree Cannot Have a Cycle (cont.) D A B C Another path from A to C (passes through C once) E

Example of a Tree In a tree, every pair of linked nodes have a parent-child relationship (the parent is closer to the root) root A C B D G E F

Example of a Tree (cont.) For example, C is a parent of G root A C B D G E F

Example of a Tree (cont.) root A E and F are children of D C B D G E F

Example of a Tree (cont.) The root node is the only node that has no parent. root A C B D G E F

Example of a Tree (cont.) Leaf nodes (or leaves for short) have no children. root A C B D G E F

Subtrees A subtree is a part of a tree that is a tree in itself root A C I K D E F J G H subtree

Subtrees (cont.) It normally includes each node reachable from its root. root A B C I K D E F J G H subtree

Subtrees (cont.) Even though this looks like a subtree in itself… root D E F J G H

Subtrees (cont.) G and H are reachable from the root. root A B C I K D J G H

Subtrees (cont.) So, normally, G and H would be a part of the subtree. root A B C I K D E F J G H

Binary Trees A binary tree is a tree in which each node can only have up to two children…

Not a Binary Tree root A B C I K D E F J G H

Example of a Binary Tree root A B C I K E J G H

Example of a Binary Tree (cont.) The links in a tree are often called edges root A B C I K E J G H

Levels root level 0 A level 1 B C level 2 I K E level 3 J G H The level of a node is the number of edges in the path from the root node to this node

Full Binary Tree root A B C D E F G H I J K L M N O In a full binary tree, each node has two children except for the nodes on the last level, which are leaf nodes

Complete Binary Trees A complete binary tree is a binary tree that is either a full binary tree OR a tree that would be a full binary tree but it is missing the rightmost nodes on the last level

Complete Binary Trees (cont.) root A B C D E F G H I

Complete Binary Trees (cont.) root A B C D E F G H I J K L

Complete Binary Trees (cont.) A full binary tree is also a complete binary tree. root A B C D E F G H I J K L M N O

Heaps A heap is a complete binary tree in which the value of each node is greater than or equal to the values of its children (if any) Technically, this is called a maxheap In a minheap, the value of each node is less than or equal to the values of its children A maxheap can be easily modified to make a minheap In our discussion, the word “heap” will refer to a maxheap

Heaps (cont.) The element stored in each node of a heap can be an object When we talk about the value of a node, we are really talking about some data member of the object that we want to prioritize For example, in employee objects, we may want to prioritize age or salary

Example of a Heap root 46 39 28 16 32 24 2 Only the data member is shown that we want to prioritize 14 3 29

Example of a Heap (cont.) root 46 39 28 16 32 24 2 Where is the greatest value in a heap always found? 14 3 29

Example of a Heap (cont.) root 46 39 28 16 32 24 2 Where is the greatest value in a heap always found? 14 3 29

Dequeue Dequeuing the object with the greatest value appears to be a ( 1 ) operation However, after removing the object, we must turn the resultant structure into a heap again, for the next dequeue Fortunately, it only takes O( lg n ) time to turn the structure back into a heap again (which is why dequeue in a heap is a O( lg n ) operation

Dequeue (cont.) root 46 39 28 16 32 24 2 14 3 29 15 5

Dequeue (cont.) root 46 39 28 16 32 24 2 Save the root object in remElement 14 3 29 15 5

Dequeue (cont.) root remElement: 46 46 39 28 16 32 24 2 Save the root object in remElement 14 3 29 15 5

Dequeue (cont.) root remElement: 46 46 39 28 16 32 24 2 14 3 29 15 5

Dequeue (cont.) root remElement: 46 46 39 28 16 32 24 2 Copy object in last node into root object 14 3 29 15 5

Dequeue (cont.) root remElement: 46 46 39 28 16 32 24 2 Copy object in last node into root object 14 3 29 15 5

Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 Copy object in last node into root object 14 3 29 15 5

Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 14 3 29 15 5

Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 Remove last node 14 3 29 15 5

Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 Remove last node 14 3 29 15

Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 14 3 29 15

Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 5 Greatest Child 39 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 5 Greatest Child 39 39 > 5, so swap 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 Notice that 39 is correctly placed – it is guaranteed to be greater than or equal to its two children 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 It was the greatest child, so it is greater than the other child (28) 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 It is greater than the element it was swapped with (5), or else we wouldn’t have swapped. 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 At this point, we repeat the process, using 5 as the parent. 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 Greatest Child 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 32 > 5, so swap 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 28 5 32 16 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 28 32 5 16 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 14 3 29 15

Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15 Greatest Child

Dequeue (cont.) root remElement: 46 39 32 28 29 > 5, so swap 16 5 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15 Greatest Child

Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15

Dequeue (cont.) root remElement: 46 39 32 28 16 24 2 5 29 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 15

Dequeue (cont.) root remElement: 46 39 32 28 16 24 2 29 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 5 14 3 15

Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 5 15

Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 14 3 5 15

Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 The final result is a heap! 14 3 5 15

Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 Sometimes, it is not necessary to swap all the way down through the heap 14 3 5 15

Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 If 5 would have been greater than or equal to both of its children, we would stop there 14 3 5 15

Heapify The process of swapping downwards to form a new heap is called heapifying When, we heapify, it is important that the rest of the structure is a heap, except for the root node that we are starting off with; otherwise, a new heap won’t be formed A loop is used for heapifying; the number of times through the loop is always lg n or less, which gives the O( lg n ) complexity Each time we swap downwards, the number of nodes we can travel to is reduced by approximately half

Enqueue root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Create a new node in the last position 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Create a new node in the last position 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Place the value to enqueue in the last node 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Place the value to enqueue in the last node 14 3 5 15 37

Enqueue (cont.) root 39 32 28 16 29 24 2 14 3 5 15 37

Enqueue (cont.) root 39 32 28 16 29 24 2 If 37 is larger than its parent, swap 14 3 5 15 37

Enqueue (cont.) root 39 32 28 37 > 24, so swap 16 29 24 2 If 37 is larger than its parent, swap 14 3 5 15 37

Enqueue (cont.) root 39 32 28 16 29 24 2 14 3 5 15 37

Enqueue (cont.) root 39 32 28 16 29 2 24 37 14 3 5 15

Enqueue (cont.) root 39 32 28 16 29 2 37 24 14 3 5 15

Enqueue (cont.) root 39 32 28 16 29 37 2 14 3 5 15 24

Enqueue (cont.) root 39 32 28 16 29 37 2 14 3 5 15 24

Enqueue (cont.) root 39 32 28 16 29 37 2 If 37 is larger than its parent, swap 14 3 5 15 24

Enqueue (cont.) root 39 37 > 28, so swap 32 28 16 29 37 2 If 37 is larger than its parent, swap 14 3 5 15 24

Enqueue (cont.) root 39 32 28 16 29 37 2 14 3 5 15 24

Enqueue (cont.) root 39 32 28 37 16 29 2 14 3 5 15 24

Enqueue (cont.) root 39 32 28 37 16 29 2 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 Notice that 37 is guaranteed to be greater than or equal to its children 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 It was greater than the value swapped with (28), or we wouldn’t have swapped… 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 and 28 was greater than the other node (2) or it wouldn’t have been a heap… 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 so 37 must be greater than the other node (2) as well. 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 If 37 is larger than its parent, swap 14 3 5 15 24

Enqueue (cont.) root 39 37 < 39, so don’t swap 32 37 16 29 28 2 If 37 is larger than its parent, swap 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 The result is a heap! 14 3 5 15 24

Enqueue (cont.) root 39 32 37 16 29 28 2 Enqueue uses a loop, and it is a O( lg n ) operation (swapping in reverse) 14 3 5 15 24

Implementing a Heap Although it is helpful to think of a heap as a linked structure when visualizing the enqueue and dequeue operations, it is often implemented with an array Let’s number the nodes of a heap, starting with 0, going top to bottom and left to right…

Implementing a Heap (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 11 9 15 16

Heap Properties root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (left child #) = 2 * (parent #) + 1 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (left child #) = 2 * (parent #) + 1 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (left child #) = 2 * (parent #) + 1 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (left child #) = 2 * (parent #) + 1 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (right child #) = 2 * (parent #) + 2 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (right child #) = 2 * (parent #) + 2 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (right child #) = 2 * (parent #) + 2 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (right child #) = 2 * (parent #) + 2 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (parent #) = (child # - 1) / 2 (using integer division) 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (parent #) = (child # - 1) / 2 (using integer division) 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (parent #) = (child # - 1) / 2 (using integer division) 11 9 15 16

Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (parent #) = (child # - 1) / 2 (using integer division) 11 9 15 16

Array Implementation These remarkable properties of the heap allow us to place the elements into an array The red numbers on the previous slide correspond to the array indexes

Array Implementation (cont.) 46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 So now, this is our heap. It has no linked nodes, so it is much easier to work with. Let’s dequeue an element. The highest value is stored in the root node (index 0).

Array Implementation (cont.) 46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46

Array Implementation (cont.) 46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Now we need to move the object at the last node to the root node We need to keep track of the object in the last position of the heap using a heapsize variable remElement: 46

Array Implementation (cont.) 46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Now we need to move the object at the last node to the root node We need to keep track of the object in the last position of the heap using a heapsize variable remElement: 46 heapsize: 17

Array Implementation (cont.) 46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 Now we can access the object in the last node using elements[ heapsize – 1 ] and assign it to elements[ 0 ] heapsize: 17

Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 Now we can access the object in the last node using elements[ heapsize – 1 ] and assign it to elements[ 0 ] heapsize: 17

Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 Next, decrement the heap size heapsize: 17

Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 Next, decrement the heap size heapsize: 16

Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 The value at index 16 can’t be used anymore; it will be overwritten on the next enqueue remElement: 46 heapsize: 16

Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Now, we need to find the greatest child of node 0 and compare it to 9. But how do we get the greatest child? remElement: 46 heapsize: 16

Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 By using the formulas we noted earlier (this is why an array can be used)… heapsize: 16

Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 0 + 1 = 1 (right child #) = 2*(parent #) + 2 = 2 * 0 + 2 = 2 heapsize: 16

Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 0 + 1 = 1 (right child #) = 2*(parent #) + 2 = 2 * 0 + 2 = 2 heapsize: 16

Array Implementation (cont.) Greatest Child 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 0 + 1 = 1 (right child #) = 2*(parent #) + 2 = 2 * 0 + 2 = 2 heapsize: 16

Array Implementation (cont.) Greatest Child 39 > 9, so swap 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 0 + 1 = 1 (right child #) = 2*(parent #) + 2 = 2 * 0 + 2 = 2 heapsize: 16

Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 If the greatest child of 9 is greater than 9, then swap heapsize: 16

Array Implementation (cont.) 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 1 + 1 = 3 (right child #) = 2*(parent #) + 2 = 2 * 1 + 2 = 4 heapsize: 16

Array Implementation (cont.) 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 1 + 1 = 3 (right child #) = 2*(parent #) + 2 = 2 * 1 + 2 = 4 heapsize: 16

Array Implementation (cont.) Greatest Child 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 1 + 1 = 3 (right child #) = 2*(parent #) + 2 = 2 * 1 + 2 = 4 heapsize: 16

Array Implementation (cont.) Greatest Child 32 > 9, so swap 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 1 + 1 = 3 (right child #) = 2*(parent #) + 2 = 2 * 1 + 2 = 4 heapsize: 16

Array Implementation (cont.) 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 9 32 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 9 32 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 9 32 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 32 9 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 If the greatest child of 9 is greater than 9, then swap heapsize: 16

Array Implementation (cont.) 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 4 + 1 = 9 (right child #) = 2*(parent #) + 2 = 2 * 4 + 2 = 10 heapsize: 16

Array Implementation (cont.) 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 4 + 1 = 9 (right child #) = 2*(parent #) + 2 = 2 * 4 + 2 = 10 heapsize: 16

Array Implementation (cont.) Greatest Child 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 4 + 1 = 9 (right child #) = 2*(parent #) + 2 = 2 * 4 + 2 = 10 heapsize: 16

Array Implementation (cont.) Greatest Child 29 > 9, so swap 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 4 + 1 = 9 (right child #) = 2*(parent #) + 2 = 2 * 4 + 2 = 10 heapsize: 16

Array Implementation (cont.) 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 29 9 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16

Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 If the greatest child of 9 is greater than 9, then swap heapsize: 16

Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 9 + 1 = 19 heapsize: 16

Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 9 + 1 = 19 heapsize: 16 19 > heapsize

Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 so 9 must be a leaf node (we can stop) remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 9 + 1 = 19 heapsize: 16

Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 An enqueue is done by placing the new element at elements[ heapsize ], then swapping upwards heapsize: 16

Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 When enqueuing, the parent is always found by using the parent formula: (parent #) = (child # - 1 ) / 2 heapsize: 16

Reducing the Work in a Swap A swap (using simple assignments) cannot just involve two statements: elements[ i ] = elements[ j ]; elements[ j ] = elements[ i ]; On the first line, the value in elements[ i ] is lost; both elements will have the value in elements[ j ]

Reducing the Work in a Swap (cont.) A swap (using simple assignments) would normally involve three statements: temp = elements[ i ]; elements[ i ] = elements[ j ]; elements[ j ] = temp;

Reducing the Work in a Swap (cont.) In an enqueue or a dequeue for a heap, we do not really have to use this three-assignment swap (although it helped to visualize how the enqueue and dequeue process worked) We can save the value we are swapping upwards or downwards Let’s look at an enqueue…

Enqueue root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Create a new node in the last position 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Create a new node in the last position 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 We just “pretend” that 37 is here 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 37 > 24, so we pretend to swap (we just copy 24) 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 24 14 3 5 15

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 We now “pretend” 37 has been placed here 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 and we compare 37 to 28 to see if we should “swap” again 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 37 > 28, so we do a one-assignment swap again 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 28 16 29 24 2 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 We “pretend” 37 is here 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 and compare 37 to 39 to see if we should “swap” again 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 This time we shouldn’t swap… 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 but we have one final assignment 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 but we have one final assignment 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 37 16 29 28 2 but we have one final assignment 14 3 5 15 24

Enqueue (cont.) root value to enqueue: 37 39 32 37 16 29 28 2 (we can stop pretending) 14 3 5 15 24

Dequeue root 39 32 37 16 29 28 2 The same technique can be used when swapping downward 14 3 5 15 24

Parent-Child Formulas Parent-child formulas can also be sped up: (left child #) = (parent #) << 1 + 1 (right child #) = (left child #) + 1 when finding the greatest child, the left and right children are always found together (parent #) = (child # - 1) >> 1 using the shift operator is the same as integer division

PriorityQueue.h 1 #include "Array.h" 2 3 template <class DataType> 4 class PriorityQueue 5 { 6 public: 7 PriorityQueue( ); 8 PriorityQueue( const Array<DataType> & arr ); 9 void enqueue( const DataType & newElement ); 10 bool dequeue( DataType & remElement ); 11 bool isEmpty( ) const; 12 void makeEmpty( );

PriorityQueue.h (cont.) 14 private: 15 Array<DataType> elements; 16 int heapsize; 17 inline void heapify( int i ); 18 }; 19 20 #include "PriorityQueue.cpp"

Two Constructors There are two constructors in PriorityQueue.h The first constructor just initializes an empty heap…

First Constructor 1 template <class DataType> 2 PriorityQueue<DataType>::PriorityQueue( ) 3 : elements( 2 ), heapsize( 0 ) 4 { 5 }

Forming an Initial Heap The second constructor makes a heap out of an initial array A heap of size n can be made by enqueuing n elements one at a time – but it would take O( n lg n ) time (even though n starts off as small) A faster method can be used to make a heap out of an initial array in ( n ) time The heap will be shown as a linked tree, because it is easier to visualize how the method works – but in actuality, when we use the method, we use it on the array

Forming an Initial Heap (cont.) 6 31 5 34 34 11 7 7 12 39 38 5 32 1 34 27 16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Suppose the initial array looks like this. It is not initially a heap, but by rearranging the elements, it will be. We will look at the tree form of this to see why the method works.

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 Drawing it this way, we can easily see that the initial array is not a heap 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 We realize that nodes 8 through 16 are subheaps of only one node. 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 We want to heapify starting with the parent of the last leaf node. 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 To find the last leaf node, we use heapsize – 1 (17 – 1 = 16) 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 Then the parent of the last node is (16 – 1) / 2 = 7 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 Heapifying works only if the structure is a heap except for possibly the root. 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 This is true for the structure rooted at index 7, so we can use heapify on it… 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 27 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 7 16 This is true for the structure rooted at index 7, so we can use heapify on it… 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 27 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 7 16 Then we decrement index 7 and use heapify again… 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 27 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 7 16 Then we decrement index 7 and use heapify again… 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 34 3 4 5 6 27 12 39 38 5 32 1 7 7 8 9 10 11 12 13 14 7 16 Then we decrement index 7 and use heapify again… 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 34 3 4 5 6 27 12 39 38 5 32 1 7 7 8 9 10 11 12 13 14 7 16 This process continues until we heapify at the root 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 34 3 4 5 6 27 12 39 38 5 32 1 7 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 32 34 3 4 5 6 27 12 39 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 32 34 3 4 5 6 27 12 39 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 39 32 34 3 4 5 6 27 12 34 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 39 32 34 3 4 5 6 27 12 34 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 39 32 34 3 4 5 6 27 12 34 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 already a heap 15 16

Forming an Initial Heap (cont.) root 6 31 5 1 2 34 39 32 34 3 4 5 6 27 12 34 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) temp root 6 31 5 1 2 34 39 32 34 3 4 5 6 27 12 34 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 one-assignment swaps 15 16

Forming an Initial Heap (cont.) root 6 31 34 1 2 34 39 32 7 3 4 5 6 27 12 34 38 5 11 1 5 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) root 6 31 34 1 2 34 39 32 7 3 4 5 6 27 12 34 38 5 11 1 5 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) root 6 31 34 1 2 34 39 32 7 3 4 5 6 27 12 34 38 5 11 1 5 7 8 9 10 11 12 13 14 7 16 temp 15 16

Forming an Initial Heap (cont.) root 6 39 34 1 2 34 38 32 7 3 4 5 6 27 12 34 31 5 11 1 5 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) root 6 39 34 1 2 34 38 32 7 3 4 5 6 27 12 34 31 5 11 1 5 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) root 6 39 34 1 2 34 38 32 7 3 4 5 6 27 12 34 31 5 11 1 5 7 8 9 10 11 12 13 14 7 16 temp 15 16

Forming an Initial Heap (cont.) root 39 38 34 1 2 34 34 32 7 3 4 5 6 27 12 6 31 5 11 1 5 7 8 9 10 11 12 13 14 7 16 15 16

Forming an Initial Heap (cont.) root 39 38 34 1 2 34 34 32 7 3 4 5 6 27 12 6 31 5 11 1 5 7 8 9 10 11 12 13 14 7 16 The result is a heap 15 16

2nd Constructor 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 }

2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } ((heapsize – 1) – 1)

2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } ((heapsize – 1) – 1) index of last node

2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } index of parent of last node

2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } makes a heap out of the array

2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } finds the least power of 2 that is greater than or equal to the array size

2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } if the array size is not already a power of 2, change it to a power of 2 (helps with maintenance)

Enqueue 20 template <class DataType> 21 void PriorityQueue<DataType>::enqueue( 22 const DataType & newElement ) 23 { 24 if ( heapsize == elements.length( ) ) 25 elements.changeSize( elements.length( ) << 1 ); 26 enqueue continued…

Enqueue (cont.) i will be used as the current position 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) heapsize – 1 is the index of the last position – we start i at the next position beyond the last position (where we start the enqueue) 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) We are pretending that we placed newElement in this position (newElement is passed in from the client) 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) This is the parent of the current position. 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) We compare newElement with the parent value… 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) and do a one-assignment swap if newElement is greater 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) Then, the current position becomes the parent of the current position 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) Afterwards, we compare newElement with the parent of the new current position 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) When the loop stops, i is either 0 (the root)… 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) or newElement (which we pretend is at the current position i) is less than or equal to its parent 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) Either way, we found the place to put newElement 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) Then, since we enqueued, heapsize is increased by one 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Enqueue (cont.) Notice that this is an overloaded operator for elements that are objects. 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }

Dequeue 35 template <class DataType> 36 bool PriorityQueue<DataType>::dequeue( 37 DataType & remElement ) 38 { 39 if ( !heapsize ) 40 return false; 41 remElement = elements[ 0 ]; 42 heapsize--; 43 elements[ 0 ] = elements[ heapsize ]; 44 heapify( 0 ); dequeue continued…

Dequeue (cont.) 45 int trysize = elements.length( ); 46 while ( ( heapsize <= trysize >> 2 ) && trysize > 2 ) 47 trysize >>= 1; 48 49 if ( trysize < elements.length( ) ) { 50 try { 51 elements.changeSize( trysize ); 52 } 53 catch ( ... ) {} 54 } 55 56 return true; 57 }

isEmpty 58 template <class DataType> 59 bool PriorityQueue<DataType>::isEmpty( ) const 60 { 61 return !heapsize; 62 }

makeEmpty 63 template <class DataType> 64 void PriorityQueue<DataType>::makeEmpty( ) 65 { 66 heapsize = 0; 67 try { 68 elements.changeSize( 2 ); 69 } 70 catch( ... ) { } 71 }

Heapify 72 template <class DataType> 73 inline void PriorityQueue<DataType>::heapify( int i ) 74 { 75 int leftChild, rightChild, largest; 76 bool stop = false; 77 78 DataType temp = elements[ i ]; 79 80 leftChild = (i << 1) + 1; heapify continued…

Heapify (cont.) 81 while ( leftChild < heapsize && !stop ) { 82 rightChild = leftChild + 1; 83 largest = ( rightChild == heapsize )? leftChild : 84 (( elements[leftChild] > elements[rightChild])? 85 leftChild : rightChild ); 86 if ( elements[ largest ] > temp ) { 87 elements[ i ] = elements[ largest ]; 88 i = largest; 89 leftChild = (i << 1) + 1; 90 } 91 else 92 stop = true; 93 } 94 elements[ i ] = temp; 95 }

Heapify (cont.) 81 while ( leftChild < heapsize && !stop ) { 82 rightChild = leftChild + 1; 83 largest = ( rightChild == heapsize )? leftChild : 84 (( elements[leftChild] > elements[rightChild])? 85 leftChild : rightChild ); 86 if ( elements[ largest ] > temp ) { 87 elements[ i ] = elements[ largest ]; 88 i = largest; 89 leftChild = (i << 1) + 1; 90 } 91 else 92 stop = true; 93 } 94 elements[ i ] = temp; 95 } We are careful that the client does not need to overload more than one operator for the class

Linked Heap If we have a large element size, we might want to consider conserving memory with a linked heap should have the same time complexities as the array-based heap

Dequeue Requirements left and right pointers in a node, so we can get the children for swapping a pointer to the last node in the heap, which needs to be maintained (updated) in ( 1 ) time

Enqueue Requirements a parent pointer in a node, for swapping upwards if necessary a ( 1 ) method of finding the place to put the initial node

List through the Heap We work with the end of the array when enqueuing or dequeuing We can work with the end of a linked list instead

List Through the Heap (cont.) shown in red A B C D E F G H I J K L M N O

Embedded Heap This isn’t a heap now, because it isn’t even a tree we have cycles We can say there is an “embedded heap” in this data structure

Required Pointers We’ll need to maintain a pointer at the end of the list called “last”, so we know where we’re supposed to form a new node (for the enqueue) and remove a node (for the dequeue) We’ll also need a pointer to the last parent, so we know which parent to attach a new node to

Adding New Nodes A lastParent B C D E last

Adding New Nodes (cont.) lastParent B C D E F last

Adding New Nodes (cont.) lastParent B C D E F last

Adding New Nodes (cont.) lastParent B C D E F last

Adding New Nodes (cont.) lastParent B C D E F G last

Adding New Nodes (cont.) lastParent B C D E F G last

Adding New Nodes (cont.) lastParent B C D E F G last

Adding New Nodes (cont.) lastParent B C D E F G lastParent = lastParent->next last

Adding New Nodes (cont.) lastParent B C D E F G lastParent = lastParent->next last

Adding New Nodes (cont.) lastParent B C D E F G last

Adding New Nodes (cont.) lastParent B C D E F G last H

Adding New Nodes (cont.) lastParent B C D E F G H last

Adding New Nodes (cont.) lastParent B C D E F G H last

Removing Nodes When we remove a node, we’ll have to bring the last pointer back one node If the list through the heap is a singly-linked list, it might not be easy to find the previous node to bring the “last” pointer back to…

Removing Nodes (cont.) A B C lastParent D E F G node to remove H I J K

Removing Nodes (cont.) If the list through the heap is doubly-linked, it’s easy to find the node to set “last” to…

Removing Nodes (cont.) A B C lastParent D E F G node to remove H I J K

Removing Nodes (cont.) Using the doubly-linked list, we can also bring lastParent back one node when we need to

Node Struct We now have five pointers in a node: template <class DataType> struct PQNode { DataType info; PQNode<DataType> *left; PQNode<DataType> *right; PQNode<DataType> *parent; PQNode<DataType> *back; PQNode<DataType> *next; };

Time Considerations Every time we need to add or remove a node, we’ll need to set a number of pointers In the array-based heap, however, we’d be copying a couple of elements on the average enqueue or dequeue (the average for array expansion / contraction) For large element sizes, it is still worth it

Memory Considerations If an element size is 20 bytes, 50% of the memory in the linked (embedded) heap is wasted If an element size is 1980 bytes, 1% of the memory in the linked (embedded) heap is wasted For large element sizes, it is a worthwhile data structure to consider

Avoiding Special Cases We can avoid handling special cases involving the root by using a “root header node” similar to the header node in the linked list The actual root node will branch off as a single child of the root header Should the actual root branch from the left or right side of the root header?

Position of Actual Root The root pointer, last pointer, and lastParent pointer will start off pointing to the root header We move the lastParent pointer forward after a right child has been attached to its node Therefore, the actual root should branch off the right side of the root header…

Starting it Off last root lastParent root header an empty heap – heap is empty when root == last

Starting it Off (cont.) last root lastParent root header an element is enqueued – “actual root” is inserted A

Starting it Off (cont.) last root lastParent root header A

Starting it Off (cont.) root lastParent root header last A

Starting it Off (cont.) root lastParent root header last A Since a node has been inserted to the right of the lastParent node, it is time to move the lastParent

Starting it Off (cont.) root root header last lastParent A Since a node has been inserted to the right of the lastParent node, it is time to move the lastParent

Starting it Off (cont.) root root header last lastParent A Now, we are in a position to insert to the left and right of this node

Starting it Off (cont.) root root header last lastParent A B

Starting it Off (cont.) root root header lastParent A last B

Starting it Off (cont.) root root header lastParent A last B

Starting it Off (cont.) root root header lastParent A last B C

Starting it Off (cont.) root root header lastParent A last B C

Starting it Off (cont.) root root header lastParent A last B C

Starting it Off (cont.) root root header A last lastParent B C

Starting it Off (cont.) root root header A last lastParent etc. B C

PriorityQueue.h We still need Array.h, since the client will pass an Array into the second constructor 1 #include "Array.h" 2 3 template <class DataType> 4 struct PQNode { 5 DataType info; 6 PQNode<DataType> *left; 7 PQNode<DataType> *right; 8 PQNode<DataType> *parent; 9 PQNode<DataType> *back; 10 PQNode<DataType> *next; 11 }; PriorityQueue.h continued…

PriorityQueue.h (cont.) 12 template <class DataType> 13 class PriorityQueue 14 { 15 public: 16 PriorityQueue( ); 17 PriorityQueue( Array<DataType> & arr ); 18 PriorityQueue( const PriorityQueue<DataType> & appq ); 19 ~PriorityQueue( ); 20 PriorityQueue<DataType> & operator =( 21 const PriorityQueue<DataType> & rpq );

PriorityQueue.h (cont.) 22 void enqueue( const DataType & newElement ); 23 bool dequeue( DataType & deqElement ); 24 bool isEmpty(); 25 void makeEmpty();

PriorityQueue.h (cont.) 26 private: 27 PQNode<DataType> rootNode; 28 PQNode<DataType> *root; 29 PQNode<DataType> *last; 30 PQNode<DataType> *lastParent; 31 bool left; 32 inline void insertNode( const DataType & inf ); 33 inline void heapify( PQNode<DataType> *current ); 34 inline void deepCopy( 35 const PriorityQueue<DataType> & original ); 36 }; 37 38 #include "PriorityQueue.cpp"

PriorityQueue.h (cont.) Used to determine which side of the lastParent node to insert on… 26 private: 27 PQNode<DataType> rootNode; 28 PQNode<DataType> *root; 29 PQNode<DataType> *last; 30 PQNode<DataType> *lastParent; 31 bool left; 32 inline void insertNode( const DataType & inf ); 33 inline void heapify( PQNode<DataType> *current ); 34 inline void deepCopy( 35 const PriorityQueue<DataType> & original ); 36 }; 37 38 #include "PriorityQueue.cpp"

PriorityQueue.h (cont.) 26 private: 27 PQNode<DataType> rootNode; 28 PQNode<DataType> *root; 29 PQNode<DataType> *last; 30 PQNode<DataType> *lastParent; 31 bool left; 32 inline void insertNode( const DataType & inf ); 33 inline void heapify( PQNode<DataType> *current ); 34 inline void deepCopy( 35 const PriorityQueue<DataType> & original ); 36 }; 37 38 #include "PriorityQueue.cpp" and also when to move the lastParent pointer

First Constructor 1 template <class DataType> 2 PriorityQueue<DataType>::PriorityQueue( ) 3 { 4 last = lastParent = root = &rootHeader; 5 root->next = NULL; 6 left = false; 7 }

Second Constructor 8 template <class DataType> 9 PriorityQueue<DataType>::PriorityQueue( 10 Array<DataType> & arr ) 11 { 12 last = lastParent = root = &rootHeader; 13 left = false; 14 15 for ( int i = 0; i < arr.length( ); i++ ) 16 insertNode( arr[ i ] ); 17 18 for ( PQNode<DataType> *current = last->parent; 19 current != root; current = current->back ) 20 heapify( current ); 21 }

Second Constructor 8 template <class DataType> 9 PriorityQueue<DataType>::PriorityQueue( 10 Array<DataType> & arr ) 11 { 12 last = lastParent = root = &rootHeader; 13 left = false; 14 15 for ( int i = 0; i < arr.length( ); i++ ) 16 insertNode( arr[ i ] ); 17 18 for ( PQNode<DataType> *current = last->parent; 19 current != root; current = current->back ) 20 heapify( current ); 21 } Makes the “embedded tree” (just inserts)

Second Constructor 8 template <class DataType> 9 PriorityQueue<DataType>::PriorityQueue( 10 Array<DataType> & arr ) 11 { 12 last = lastParent = root = &rootHeader; 13 left = false; 14 15 for ( int i = 0; i < arr.length( ); i++ ) 16 insertNode( arr[ i ] ); 17 18 for ( PQNode<DataType> *current = last->parent; 19 current != root; current = current->back ) 20 heapify( current ); 21 } Makes an embedded heap out of the embedded tree

Second Constructor 8 template <class DataType> 9 PriorityQueue<DataType>::PriorityQueue( 10 Array<DataType> & arr ) 11 { 12 last = lastParent = root = &rootHeader; 13 left = false; 14 15 for ( int i = 0; i < arr.length( ); i++ ) 16 insertNode( arr[ i ] ); 17 18 for ( PQNode<DataType> *current = last->parent; 19 current != root; current = current->back ) 20 heapify( current ); 21 } Remember that root points to the root header

Copy Constructor and Destructor 22 template <class DataType> 23 PriorityQueue<DataType>::PriorityQueue( 24 const PriorityQueue<DataType> & appq ) 25 { 26 deepCopy( appq ); 27 } 28 29 template <class DataType> 30 PriorityQueue<DataType>::~PriorityQueue( ) 31 { 32 makeEmpty( ); 33 }

Overloaded Assignment Operator 34 template <class DataType> 35 PriorityQueue<DataType> & PriorityQueue<DataType>:: 36 operator =( const PriorityQueue<DataType> & rpq ) 37 { 38 if ( this == &rpq ) 39 return *this; 40 makeEmpty( ); 41 deepCopy( rpq ); 42 return *this; 43 }

Enqueue 44 template <class DataType> 45 void PriorityQueue<DataType>::enqueue( 46 const DataType & newElement) 47 { 48 insertNode( newElement ); Places a new node at the end of the heap enqueue continued…

Enqueue (cont.) reheap upwards using one-assignment swaps 49 PQNode<DataType> *current = last, 50 *parent = current->parent; 51 while ( parent != root && newElement > parent->info ) { 52 current->info = parent->info; 53 current = parent; 54 parent = current->parent; 55 } 56 57 current->info = newElement; 58 }

Dequeue 59 template <class DataType> 60 bool PriorityQueue<DataType>::dequeue( 61 DataType & deqElement ) 62 { 63 if ( root == last ) 64 return false; 65 66 PQNode<DataType> *current = root->right; 67 deqElement = current->info; 68 current->info = last->info; dequeue continued…

Dequeue (cont.) 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; Code to remove the last node

Dequeue (cont.) 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; True if the next node to be inserted will be the left child of the lastParent node…

Dequeue (cont.) 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; but we are removing a node, so we have to move lastParent back

Dequeue (cont.) 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; We’ll use this pointer convention to show how the code works parent back next left right

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; last lastParent

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; last lastParent

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; last lastParent

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; last lastParent

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last

Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last

Dequeue (cont.) 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; dequeue continued…

Dequeue (cont.) toggle left whenever we have to enqueue or dequeue (it was done in the insertNode function for enqueue) 78 left = !left; 79 80 if ( root != last ) 81 heapify( current ); 82 83 return true; 84 }

Dequeue (cont.) current was set to the “actual root” previously 78 left = !left; 79 80 if ( root != last ) 81 heapify( current ); 82 83 return true; 84 }

Dequeue (cont.) if the heap isn’t empty, the last step of dequeue is to heapify 78 left = !left; 79 80 if ( root != last ) 81 heapify( current ); 82 83 return true; 84 }

isEmpty 85 template <class DataType> 86 bool PriorityQueue<DataType>::isEmpty() 87 { 88 return root == last; 89 }

makeEmpty 90 template <class DataType> 91 void PriorityQueue<DataType>::makeEmpty() 92 { 93 while ( root != last ) { 94 lastParent = last->back; 95 delete last; 96 last = lastParent; 97 } 98 99 root->next = NULL; 100 left = false; 101 } We move back through the doubly-linked list, freeing each last node along the way

makeEmpty (cont.) 90 template <class DataType> 91 void PriorityQueue<DataType>::makeEmpty() 92 { 93 while ( root != last ) { 94 lastParent = last->back; 95 delete last; 96 last = lastParent; 97 } 98 99 root->next = NULL; 100 left = false; 101 } When done, we want the last and lastParent pointers to point to the root header

makeEmpty (cont.) 90 template <class DataType> 91 void PriorityQueue<DataType>::makeEmpty() 92 { 93 while ( root != last ) { 94 lastParent = last->back; 95 delete last; 96 last = lastParent; 97 } 98 99 root->next = NULL; 100 left = false; 101 } last points to the root header when the loop stops…

makeEmpty (cont.) 90 template <class DataType> 91 void PriorityQueue<DataType>::makeEmpty() 92 { 93 while ( root != last ) { 94 lastParent = last->back; 95 delete last; 96 last = lastParent; 97 } 98 99 root->next = NULL; 100 left = false; 101 } which means that lastParent also points to the root header

insertNode 102 template <class DataType> 103 inline void PriorityQueue<DataType>::insertNode( 104 const DataType & inf ) 105 { We’ll see how the body of insertNode works on the following slides…

insertNode (cont.) root 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last

insertNode (cont.) root 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last

insertNode (cont.) root 107 last->next->back = last; lastParent 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last

insertNode (cont.) root 107 last->next->back = last; lastParent 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last

insertNode (cont.) root 108 last = last->next; lastParent last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last

insertNode (cont.) root 108 last = last->next; last lastParent 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent

insertNode (cont.) root 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent

insertNode (cont.) root 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent

insertNode (cont.) root 110 last->parent = lastParent; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent

insertNode (cont.) root 110 last->parent = lastParent; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent

insertNode (cont.) root 111 if (left) last lastParent 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent

insertNode (cont.) root 113 else { last lastParent 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent

insertNode (cont.) root 114 lastParent->right = last; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent

insertNode (cont.) root 114 lastParent->right = last; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent

insertNode (cont.) root 115 lastParent = lastParent->next; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent

insertNode (cont.) root 115 lastParent = lastParent->next; 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last

insertNode (cont.) root lastParent 117 last->info = inf; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last

insertNode (cont.) root lastParent 117 last->info = inf; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last inf was passed in as a parameter

insertNode (cont.) root lastParent 118 left = !left; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last

insertNode (cont.) root lastParent last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last

Heapify 120 template <class DataType> 121 inline void PriorityQueue<DataType>::heapify( 122 PQNode<DataType> *current ) 123 { 124 DataType temp = current->info; 125 126 PQNode<DataType> *leftc = current->left, 127 *rightc = current->right, *largest; 128 largest = (rightc == NULL)? leftc : 129 ((leftc->info > rightc->info)? leftc : rightc ); for one-assignment swaps heapify continued…

Heapify (cont.) 130 while ( (leftc != NULL) && largest->info > temp ) { 131 current->info = largest->info; 132 current = largest; 133 leftc = current->left; 134 rightc = current->right; 135 largest = (rightc == NULL)? leftc : 136 ((leftc->info > rightc->info)? leftc : rightc ); 137 } 138 139 current->info = temp; 140 } the one-assignment swap

Heapify (cont.) 130 while ( (leftc != NULL) && largest->info > temp ) { 131 current->info = largest->info; 132 current = largest; 133 leftc = current->left; 134 rightc = current->right; 135 largest = (rightc == NULL)? leftc : 136 ((leftc->info > rightc->info)? leftc : rightc ); 137 } 138 139 current->info = temp; 140 } largest points to largest child, so move current down to it

Heapify (cont.) 130 while ( (leftc != NULL) && largest->info > temp ) { 131 current->info = largest->info; 132 current = largest; 133 leftc = current->left; 134 rightc = current->right; 135 largest = (rightc == NULL)? leftc : 136 ((leftc->info > rightc->info)? leftc : rightc ); 137 } 138 139 current->info = temp; 140 }

deepCopy 141 template <class DataType> 142 inline void PriorityQueue<DataType>::deepCopy( 143 const PriorityQueue<DataType> & original ) 144 { 145 last = lastParent = root = &rootHeader; 146 root->next = NULL; 147 left = false; 148 149 PQNode<DataType> *originalptr = original.root->next; 150 for ( ; originalptr != NULL; originalptr = originalptr->next ) 153 insertNode( originalptr->info ); 154 }