Download presentation
Presentation is loading. Please wait.
Published bySurya Susanto Modified over 6 years ago
1
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
2
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
3
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
4
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)
5
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
6
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…
7
Enqueuing an Element … … 211 204 201 81 79 70 69 67 7 5
In this array of elements, each element might be an object, but only the data member considered for maximum value is shown.
8
Enqueuing an Element (cont.)
… … 211 204 201 81 79 70 69 67 7 5 Suppose that element 71 needs to be enqueued.
9
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 5 Suppose that element 71 needs to be enqueued.
10
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 5 Suppose that element 71 needs to be enqueued. We could enqueue 71 by using a loop.
11
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 5 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
12
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 5 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
13
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
14
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
15
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
16
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
17
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
18
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
19
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 7 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
20
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 67 7 7 5 i This process continues and i eventually becomes 51 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
21
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 69 20 7 5 i This process continues and i eventually becomes 51 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
22
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 69 69 20 7 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
23
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
24
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
25
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
26
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
27
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 i FALSE for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
28
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 i Now we can use: arr[ i ] = item; to enqueue the element
29
Enqueuing an Element (cont.)
item: 71 … … 211 204 201 81 79 71 70 69 20 7 5 i Now we can use: arr[ i ] = item; to enqueue the element
30
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 )
31
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
32
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
33
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)
34
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
35
Paths D F A G E B C A set of linked nodes
36
Paths (cont.) D F A G E B C There is one path from A to B
37
Paths (cont.) D F A G E B C There is no path from C to B
38
Paths (cont.) D F A G E B C There is a path from D to B
39
Paths (cont.) D F A G E B There is also a second path from D to B. C
40
Paths (cont.) D F A G E B There is also a second path from D to B. C
41
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
42
Example of a Tree root A C B D G E F
43
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
44
Example of a Cycle D A B C E
45
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
46
Tree Cannot Have a Cycle (cont.)
D A B C One path from A to C E
47
Tree Cannot Have a Cycle (cont.)
D A B C Another path from A to C (passes through C once) E
48
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
49
Example of a Tree (cont.)
For example, C is a parent of G root A C B D G E F
50
Example of a Tree (cont.)
root A E and F are children of D C B D G E F
51
Example of a Tree (cont.)
The root node is the only node that has no parent. root A C B D G E F
52
Example of a Tree (cont.)
Leaf nodes (or leaves for short) have no children. root A C B D G E F
53
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
54
Subtrees (cont.) It normally includes each node reachable from its root. root A B C I K D E F J G H subtree
55
Subtrees (cont.) Even though this looks like a subtree in itself… root
D E F J G H
56
Subtrees (cont.) G and H are reachable from the root. root A B C I K D
J G H
57
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
58
Binary Trees A binary tree is a tree in which each node can only have up to two children…
59
Not a Binary Tree root A B C I K D E F J G H
60
Example of a Binary Tree
root A B C I K E J G H
61
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
62
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
63
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
64
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
65
Complete Binary Trees (cont.)
root A B C D E F G H I
66
Complete Binary Trees (cont.)
root A B C D E F G H I J K L
67
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
68
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
69
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
70
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
71
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
72
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
73
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
74
Dequeue (cont.) root 46 39 28 16 32 24 2 14 3 29 15 5
75
Dequeue (cont.) root 46 39 28 16 32 24 2 Save the root object in remElement 14 3 29 15 5
76
Dequeue (cont.) root remElement: 46 46 39 28 16 32 24 2
Save the root object in remElement 14 3 29 15 5
77
Dequeue (cont.) root remElement: 46 46 39 28 16 32 24 2 14 3 29 15 5
78
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
79
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
80
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
81
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 14 3 29 15 5
82
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2
Remove last node 14 3 29 15 5
83
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2
Remove last node 14 3 29 15
84
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 14 3 29 15
85
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
86
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
87
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
88
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
89
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
90
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
91
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
92
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 14 3 29 15
93
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
94
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
95
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
96
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
97
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
98
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
99
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
100
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
101
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
102
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
103
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
104
Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 14 3 29 15
105
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
106
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
107
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
108
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
109
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
110
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
111
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
112
Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 14 3 5 15
113
Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2
The final result is a heap! 14 3 5 15
114
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
115
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
116
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
117
Enqueue root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15
118
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
119
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
120
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5
15
121
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
122
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
123
Enqueue (cont.) root 39 32 28 16 29 24 2 14 3 5 15 37
124
Enqueue (cont.) root 39 32 28 16 29 24 2 If 37 is larger than its parent, swap 14 3 5 15 37
125
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
126
Enqueue (cont.) root 39 32 28 16 29 24 2 14 3 5 15 37
127
Enqueue (cont.) root 39 32 28 16 29 2 24 37 14 3 5 15
128
Enqueue (cont.) root 39 32 28 16 29 2 37 24 14 3 5 15
129
Enqueue (cont.) root 39 32 28 16 29 37 2 14 3 5 15 24
130
Enqueue (cont.) root 39 32 28 16 29 37 2 14 3 5 15 24
131
Enqueue (cont.) root 39 32 28 16 29 37 2 If 37 is larger than its parent, swap 14 3 5 15 24
132
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
133
Enqueue (cont.) root 39 32 28 16 29 37 2 14 3 5 15 24
134
Enqueue (cont.) root 39 32 28 37 16 29 2 14 3 5 15 24
135
Enqueue (cont.) root 39 32 28 37 16 29 2 14 3 5 15 24
136
Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24
137
Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24
138
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
139
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
140
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
141
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
142
Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24
143
Enqueue (cont.) root 39 32 37 16 29 28 2 If 37 is larger than its parent, swap 14 3 5 15 24
144
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
145
Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24
146
Enqueue (cont.) root 39 32 37 16 29 28 2 The result is a heap! 14 3 5
15 24
147
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
148
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…
149
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
150
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
151
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
152
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
153
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
154
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
155
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
156
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
157
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
158
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
159
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
160
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) / (using integer division) 11 9 15 16
161
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) / (using integer division) 11 9 15 16
162
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) / (using integer division) 11 9 15 16
163
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) / (using integer division) 11 9 15 16
164
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
165
Array Implementation (cont.)
46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 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).
166
Array Implementation (cont.)
46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46
167
Array Implementation (cont.)
46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 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
168
Array Implementation (cont.)
46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 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
169
Array Implementation (cont.)
46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 Now we can access the object in the last node using elements[ heapsize – 1 ] and assign it to elements[ 0 ] heapsize: 17
170
Array Implementation (cont.)
9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 Now we can access the object in the last node using elements[ heapsize – 1 ] and assign it to elements[ 0 ] heapsize: 17
171
Array Implementation (cont.)
9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 Next, decrement the heap size heapsize: 17
172
Array Implementation (cont.)
9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 Next, decrement the heap size heapsize: 16
173
Array Implementation (cont.)
9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 The value at index 16 can’t be used anymore; it will be overwritten on the next enqueue remElement: 46 heapsize: 16
174
Array Implementation (cont.)
9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 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
175
Array Implementation (cont.)
9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 By using the formulas we noted earlier (this is why an array can be used)… heapsize: 16
176
Array Implementation (cont.)
9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 1 (right child #) = 2*(parent #) + 2 = 2 * = 2 heapsize: 16
177
Array Implementation (cont.)
9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 1 (right child #) = 2*(parent #) + 2 = 2 * = 2 heapsize: 16
178
Array Implementation (cont.)
Greatest Child 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 1 (right child #) = 2*(parent #) + 2 = 2 * = 2 heapsize: 16
179
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 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 1 (right child #) = 2*(parent #) + 2 = 2 * = 2 heapsize: 16
180
Array Implementation (cont.)
9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
181
Array Implementation (cont.)
39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 9 remElement: 46 heapsize: 16
182
Array Implementation (cont.)
39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 9 remElement: 46 heapsize: 16
183
Array Implementation (cont.)
39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
184
Array Implementation (cont.)
39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 If the greatest child of 9 is greater than 9, then swap heapsize: 16
185
Array Implementation (cont.)
39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 3 (right child #) = 2*(parent #) + 2 = 2 * = 4 heapsize: 16
186
Array Implementation (cont.)
39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 3 (right child #) = 2*(parent #) + 2 = 2 * = 4 heapsize: 16
187
Array Implementation (cont.)
Greatest Child 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 3 (right child #) = 2*(parent #) + 2 = 2 * = 4 heapsize: 16
188
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 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 3 (right child #) = 2*(parent #) + 2 = 2 * = 4 heapsize: 16
189
Array Implementation (cont.)
39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
190
Array Implementation (cont.)
9 32 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
191
Array Implementation (cont.)
9 32 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
192
Array Implementation (cont.)
9 32 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
193
Array Implementation (cont.)
32 9 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
194
Array Implementation (cont.)
39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
195
Array Implementation (cont.)
39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 If the greatest child of 9 is greater than 9, then swap heapsize: 16
196
Array Implementation (cont.)
39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 9 (right child #) = 2*(parent #) + 2 = 2 * = 10 heapsize: 16
197
Array Implementation (cont.)
39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 9 (right child #) = 2*(parent #) + 2 = 2 * = 10 heapsize: 16
198
Array Implementation (cont.)
Greatest Child 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 9 (right child #) = 2*(parent #) + 2 = 2 * = 10 heapsize: 16
199
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 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 9 (right child #) = 2*(parent #) + 2 = 2 * = 10 heapsize: 16
200
Array Implementation (cont.)
39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
201
Array Implementation (cont.)
9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
202
Array Implementation (cont.)
9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
203
Array Implementation (cont.)
9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
204
Array Implementation (cont.)
9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
205
Array Implementation (cont.)
29 9 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
206
Array Implementation (cont.)
39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 remElement: 46 heapsize: 16
207
Array Implementation (cont.)
39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 remElement: 46 If the greatest child of 9 is greater than 9, then swap heapsize: 16
208
Array Implementation (cont.)
39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 19 heapsize: 16
209
Array Implementation (cont.)
39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 19 heapsize: 16 19 > heapsize
210
Array Implementation (cont.)
39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 so 9 must be a leaf node (we can stop) remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * = 19 heapsize: 16
211
Array Implementation (cont.)
39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 An enqueue is done by placing the new element at elements[ heapsize ], then swapping upwards heapsize: 16
212
Array Implementation (cont.)
39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 When enqueuing, the parent is always found by using the parent formula: (parent #) = (child # - 1 ) / 2 heapsize: 16
213
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 ]
214
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;
215
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…
216
Enqueue root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15
217
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
218
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
219
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
220
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
221
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5
15
222
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 24 14 3
5 15
223
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5
15 24
224
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
225
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
226
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
227
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5
15 24
228
Enqueue (cont.) root value to enqueue: 37 39 32 28 28 16 29 24 2 14 3
5 15 24
229
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 14 3 5
15 24
230
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2
We “pretend” 37 is here 14 3 5 15 24
231
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
232
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
233
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
234
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
235
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
236
Enqueue (cont.) root value to enqueue: 37 39 32 37 16 29 28 2
(we can stop pretending) 14 3 5 15 24
237
Dequeue root 39 32 37 16 29 28 2 The same technique can be used when swapping downward 14 3 5 15 24
238
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
239
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( );
240
PriorityQueue.h (cont.)
14 private: 15 Array<DataType> elements; 16 int heapsize; 17 inline void heapify( int i ); 18 }; 19 20 #include "PriorityQueue.cpp"
241
Two Constructors There are two constructors in PriorityQueue.h
The first constructor just initializes an empty heap…
242
First Constructor 1 template <class DataType>
2 PriorityQueue<DataType>::PriorityQueue( ) 3 : elements( 2 ), heapsize( 0 ) 4 { 5 }
243
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
244
Forming an Initial Heap (cont.)
6 31 5 34 34 11 7 7 12 39 38 5 32 1 34 27 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.
245
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
246
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
247
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
248
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
249
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
250
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
251
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
252
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
253
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
254
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
255
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
256
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
257
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
258
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
259
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
260
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
261
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
262
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
263
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
264
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
265
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
266
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
267
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
268
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
269
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
270
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
271
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
272
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
273
2nd Constructor 6 template <class DataType>
7 PriorityQueue<DataType>::PriorityQueue( 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 }
274
2nd Constructor (cont.) 6 template <class DataType>
7 PriorityQueue<DataType>::PriorityQueue( 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)
275
2nd Constructor (cont.) 6 template <class DataType>
7 PriorityQueue<DataType>::PriorityQueue( 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
276
2nd Constructor (cont.) 6 template <class DataType>
7 PriorityQueue<DataType>::PriorityQueue( 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
277
2nd Constructor (cont.) 6 template <class DataType>
7 PriorityQueue<DataType>::PriorityQueue( 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
278
2nd Constructor (cont.) 6 template <class DataType>
7 PriorityQueue<DataType>::PriorityQueue( 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
279
2nd Constructor (cont.) 6 template <class DataType>
7 PriorityQueue<DataType>::PriorityQueue( 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)
280
Enqueue 20 template <class DataType>
21 void PriorityQueue<DataType>::enqueue( const DataType & newElement ) 23 { 24 if ( heapsize == elements.length( ) ) elements.changeSize( elements.length( ) << 1 ); 26 enqueue continued…
281
Enqueue (cont.) i will be used as the current position
27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
282
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 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
283
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 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
284
Enqueue (cont.) This is the parent of the current position.
27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
285
Enqueue (cont.) We compare newElement with the parent value…
27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
286
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 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
287
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 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
288
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 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
289
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 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
290
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 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
291
Enqueue (cont.) Either way, we found the place to put newElement
27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
292
Enqueue (cont.) Then, since we enqueued, heapsize is increased by one
27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
293
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 ]; i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
294
Dequeue 35 template <class DataType>
36 bool PriorityQueue<DataType>::dequeue( 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…
295
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 }
296
isEmpty 58 template <class DataType>
59 bool PriorityQueue<DataType>::isEmpty( ) const 60 { 61 return !heapsize; 62 }
297
makeEmpty 63 template <class DataType>
64 void PriorityQueue<DataType>::makeEmpty( ) 65 { 66 heapsize = 0; 67 try { 68 elements.changeSize( 2 ); 69 } 70 catch( ... ) { } 71 }
298
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…
299
Heapify (cont.) 81 while ( leftChild < heapsize && !stop ) {
82 rightChild = leftChild + 1; 83 largest = ( rightChild == heapsize )? leftChild : (( 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 }
300
Heapify (cont.) 81 while ( leftChild < heapsize && !stop ) { 82 rightChild = leftChild + 1; 83 largest = ( rightChild == heapsize )? leftChild : (( 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
301
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
302
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
303
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
304
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
305
List Through the Heap (cont.)
shown in red A B C D E F G H I J K L M N O
306
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
307
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
308
Adding New Nodes A lastParent B C D E last
309
Adding New Nodes (cont.)
lastParent B C D E F last
310
Adding New Nodes (cont.)
lastParent B C D E F last
311
Adding New Nodes (cont.)
lastParent B C D E F last
312
Adding New Nodes (cont.)
lastParent B C D E F G last
313
Adding New Nodes (cont.)
lastParent B C D E F G last
314
Adding New Nodes (cont.)
lastParent B C D E F G last
315
Adding New Nodes (cont.)
lastParent B C D E F G lastParent = lastParent->next last
316
Adding New Nodes (cont.)
lastParent B C D E F G lastParent = lastParent->next last
317
Adding New Nodes (cont.)
lastParent B C D E F G last
318
Adding New Nodes (cont.)
lastParent B C D E F G last H
319
Adding New Nodes (cont.)
lastParent B C D E F G H last
320
Adding New Nodes (cont.)
lastParent B C D E F G H last
321
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…
322
Removing Nodes (cont.) A B C lastParent D E F G node to remove H I J K
323
Removing Nodes (cont.) If the list through the heap is doubly-linked, it’s easy to find the node to set “last” to…
324
Removing Nodes (cont.) A B C lastParent D E F G node to remove H I J K
325
Removing Nodes (cont.) Using the doubly-linked list, we can also bring lastParent back one node when we need to
326
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; };
327
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
328
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
329
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?
330
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…
331
Starting it Off last root lastParent root header
an empty heap – heap is empty when root == last
332
Starting it Off (cont.) last root lastParent root header
an element is enqueued – “actual root” is inserted A
333
Starting it Off (cont.) last root lastParent root header A
334
Starting it Off (cont.) root lastParent root header last A
335
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
336
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
337
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
338
Starting it Off (cont.) root root header last lastParent A B
339
Starting it Off (cont.) root root header lastParent A last B
340
Starting it Off (cont.) root root header lastParent A last B
341
Starting it Off (cont.) root root header lastParent A last B C
342
Starting it Off (cont.) root root header lastParent A last B C
343
Starting it Off (cont.) root root header lastParent A last B C
344
Starting it Off (cont.) root root header A last lastParent B C
345
Starting it Off (cont.) root root header A last lastParent etc. B C
346
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…
347
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 =( const PriorityQueue<DataType> & rpq );
348
PriorityQueue.h (cont.)
22 void enqueue( const DataType & newElement ); 23 bool dequeue( DataType & deqElement ); 24 bool isEmpty(); 25 void makeEmpty();
349
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( const PriorityQueue<DataType> & original ); 36 }; 37 38 #include "PriorityQueue.cpp"
350
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( const PriorityQueue<DataType> & original ); 36 }; 37 38 #include "PriorityQueue.cpp"
351
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( const PriorityQueue<DataType> & original ); 36 }; 37 38 #include "PriorityQueue.cpp" and also when to move the lastParent pointer
352
First Constructor 1 template <class DataType>
2 PriorityQueue<DataType>::PriorityQueue( ) 3 { 4 last = lastParent = root = &rootHeader; 5 root->next = NULL; 6 left = false; 7 }
353
Second Constructor 8 template <class DataType>
9 PriorityQueue<DataType>::PriorityQueue( 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 }
354
Second Constructor 8 template <class DataType>
9 PriorityQueue<DataType>::PriorityQueue( 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)
355
Second Constructor 8 template <class DataType>
9 PriorityQueue<DataType>::PriorityQueue( 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
356
Second Constructor 8 template <class DataType>
9 PriorityQueue<DataType>::PriorityQueue( 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
357
Copy Constructor and Destructor
22 template <class DataType> 23 PriorityQueue<DataType>::PriorityQueue( const PriorityQueue<DataType> & appq ) 25 { 26 deepCopy( appq ); 27 } 28 29 template <class DataType> 30 PriorityQueue<DataType>::~PriorityQueue( ) 31 { 32 makeEmpty( ); 33 }
358
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 }
359
Enqueue 44 template <class DataType>
45 void PriorityQueue<DataType>::enqueue( const DataType & newElement) 47 { 48 insertNode( newElement ); Places a new node at the end of the heap enqueue continued…
360
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 }
361
Dequeue 59 template <class DataType>
60 bool PriorityQueue<DataType>::dequeue( 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…
362
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
363
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…
364
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
365
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
366
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
367
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
368
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
369
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
370
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
371
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
372
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
373
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
374
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
375
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
376
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
377
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…
378
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 }
379
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 }
380
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 }
381
isEmpty 85 template <class DataType>
86 bool PriorityQueue<DataType>::isEmpty() 87 { 88 return root == last; 89 }
382
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
383
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
384
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…
385
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
386
insertNode 102 template <class DataType>
103 inline void PriorityQueue<DataType>::insertNode( const DataType & inf ) 105 { We’ll see how the body of insertNode works on the following slides…
387
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
388
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
389
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
390
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
391
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
392
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
393
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
394
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
395
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
396
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
397
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
398
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
399
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
400
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
401
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
402
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
403
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
404
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last inf was passed in as a parameter
405
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
406
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) lastParent->left = last; 113 else { lastParent->right = last; lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
407
Heapify 120 template <class DataType>
121 inline void PriorityQueue<DataType>::heapify( PQNode<DataType> *current ) 123 { 124 DataType temp = current->info; 125 126 PQNode<DataType> *leftc = current->left, *rightc = current->right, *largest; 128 largest = (rightc == NULL)? leftc : 129 ((leftc->info > rightc->info)? leftc : rightc ); for one-assignment swaps heapify continued…
408
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 : ((leftc->info > rightc->info)? leftc : rightc ); } 138 139 current->info = temp; 140 } the one-assignment swap
409
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 : ((leftc->info > rightc->info)? leftc : rightc ); } 138 139 current->info = temp; 140 } largest points to largest child, so move current down to it
410
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 : ((leftc->info > rightc->info)? leftc : rightc ); } 138 139 current->info = temp; 140 }
411
deepCopy 141 template <class DataType>
142 inline void PriorityQueue<DataType>::deepCopy( 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 }
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.