You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: slides/priorityqueue/indexed_priority_queue.txt
+43-42Lines changed: 43 additions & 42 deletions
Original file line number
Diff line number
Diff line change
@@ -1,8 +1,9 @@
1
-
Hello and welcome, my name is William and today's data structure we're going to cover is probably one of the most useful you'll encounter and also one of my personal favorites and it is the indexed priority queue.
1
+
[intro sound]
2
+
Hello and welcome, my name is William, and today's data structure is the indexed priority queue. This is going to prove to be a very useful data structure that you wished you would have known about a long time ago. It also happens to be one of my personal favorites, so I'm quite excited.
2
3
3
-
Just before we get started, this video builds off concepts from the previous priority queue videos which simply go over the basics. Strictly speaking you can probably get by without watching all those as I will do a quick recap, but for those who want to know PQs in full detail check out the description for links to those.
4
+
Just before we get started, this video builds off concepts from the previous priority queue videos which simply go over the basics. Strictly speaking, you can probably get by without watching all those as I will do a quick recap, but for those who want to know PQs in full detail please check out the description below for links to those videos.
4
5
5
-
So what exactly is an IPQ? It is a traditional priority queue variant which on top of having all the regular PQ operations it also supports quick updates and deletions of key-value pairs. So the big problem the IPQ solves is being able to quickly lookup and dynamically change the values in your PQ on the fly which is often useful.
6
+
So what exactly is an IPQ? Well, it is a traditional priority queue variant which on top of having all the regular PQ operations it also supports quick updates and deletions of key-value pairs. So the big problem the IPQ solves is being able to quickly lookup and dynamically change the values in your PQ on the fly which is often useful.
6
7
7
8
Let's look at an example, suppose a hospital has a waiting room with N people which need attention with different levels of priority. Each person in the waiting room has a certain condition that needs to be dealt with:
8
9
- Mary is in Labour with priority of 9
@@ -12,101 +13,101 @@ Let's look at an example, suppose a hospital has a waiting room with N people wh
12
13
- Richard fractured his wrist so priority of 5
13
14
- and lastly Leah's stomach also hurts
14
15
15
-
We want to process these patients by highest pririty first. The hospital would then serve Mary first.
16
+
We want to process these patients by highest priority first. This means the hospital would serve Mary first.
16
17
17
18
Followed by James.
18
19
19
-
Then something happens, suppose Naida’s condition worsens as she starts vomiting. Her priority gets updated to 6
20
+
However, then something happens, suppose Naida’s stomach condition worsens as she starts vomiting. Her priority gets updated to 6
20
21
21
-
Because of this Naida gets served when the hospital is finished with James.
22
+
Because of this, Naida gets served when the hospital is finished with James.
22
23
23
-
During this time Richard get impatient and leaves to go to another clinic down the street so he's no longer in the picture.
24
+
During this time, Richard gets impatient and leaves. He goes to another clinic down the street, so he no longer needs to be accounted for.
24
25
25
-
Further suppose that Akarsh goes to take a drink of water and slips on the floor and cracks his head open increasing his priority to 10.
26
+
Further suppose that Akarsh goes to take a drink of water and slips on the floor. As a result he cracks his head and needs immediate attention increasing his priority to 10.
26
27
27
28
Once Naida is dealt with, Akarsh is next.
28
29
29
30
Followed by Leah.
30
31
31
32
<press>
32
33
33
-
In the hospital example, we saw that it was very important to be able to dynamically update the priority (value) of certain people (keys).
34
-
The Indexed Priority Queue (IPQ) data structure lets us do this efficiently. The first step to using an IPQ is to assign index values to all the keys forming a bidirectional mapping.
34
+
In the hospital example, we saw that it was very important to be able to dynamically update the priority of certain people.
35
+
The Indexed Priority Queue (IPQ) data structure which lets us do this efficiently. The first step to using an IPQ is to assign index values to all the keys, thus forming a bidirectional mapping.
35
36
36
37
So if we're going to use an indexed priority queue to track who should get served next in our hospital we need to assign each person a unique key index value between 0 and N non-inclusive. Note this this mapping is intended to be bidirectional so I would advise using a bidirectional hashtable to be able to flip back and fourth between the key and its key-index. Basically any operation on the IPQ will require the associated key-index of a particular key.
37
38
38
-
So you're probably wondering why I'm saying that we need to map the keys to indexed in the domain [0, N). The reason for doing this is that typically priority queues are implemented as heaps under the hood which internally use arrays which we want to facilitate indexing into, this will become apparent shortly.
39
-
I will say though that often, and I mean very often, the keys themselves are integers in the range [0, N) so there’s no need to construct a mapping. However, it is handy to be able to support any type of key such as names.
39
+
So you're probably wondering why I'm saying that we need to map the keys to indexes in the domain [0, N). The reason for doing this is that typically priority queues are implemented as heaps under the hood which internally use arrays, which we want to facilitate indexing into; this will become apparent shortly.
40
+
I will say though, that often, and I mean very often, the keys themselves are integers in the range [0, N) so there’s no need to construct a mapping. However, it is handy to be able to support any type of keys such as names.
40
41
41
-
We can think of the IPQ as an ADT with operations we want to support. There's about a dozen or so operations we want out IPQ to support which are:
42
+
We can think of the IPQ as an ADT with operations we want to support. There are about a dozen or so operations we want our IPQ to support which are:
42
43
- deleting keys,
43
44
- getting the value associated with a key
44
45
- checking if a key exists in the PQ
45
-
- getting the key index with the smallest or largest value
46
-
- getting the smallest or largest value in the PQ
46
+
- getting the key index with the smallest value
47
+
- getting the smallest value in the PQ
47
48
- being able to insert and update key-value pairs
48
-
- and finally the specialized update operations increase and decrease key.
49
-
For all these operations you need the key-index associated with any particular key you're dealing with. Throughout these slides I will be denoting the key-index simply as the variable 'ki' to distinguish it from other index values.
49
+
- and finally, the specialized update operations increase and decrease key which we will talk about later.
50
+
For all these operations, you need the key-index associated with the particular key you're dealing with. Throughout these slides I will be denoting the key-index simply as the variable 'ki' to distinguish it from other index values.
50
51
51
-
An IPQ can be implemented in several wayswith really good time complexities using specialized heap structures, but together we're going to cover the binary heap implementation for simplicity. So you'll notice that the time complexity for all the operations here are either constant or logarithmic which is really good. In a traditional PQ the remove and update operations are linear because we are not maintaining a mapping to their position.
52
+
An IPQ can be implemented in several ways, some ways with really good time complexities using specialized heap structures, however we're going to cover the binary heap implementation for simplicity. You will notice that the time complexity for all the operations are either constant or logarithmic, which is really good. In a traditional PQ, the remove and update operations are linear because we are not maintaining a mapping to the position of where our values exist in the heap.
52
53
53
54
---------------------------
54
55
55
-
I want to spend a few slides giving a refresher on the traditional PQ data structure which only supports values, not key-value pairs so we can compare and contrast it with how a binary heap indexed PQ works because it's very similar.
56
+
Before we dive into the IPQ per se, I want to spend a few slides giving a refresher on the traditional PQ data structure, which only supports values, not key-value pairs. Still, both DSs are very similar and most people would consider them the same although there is a key difference IMO.
56
57
57
-
Recall that a very common way to represent a binary heap is with an array since every node is indexed sequentially.
58
+
Recall that a very common way to represent a binary heap is with an array, since every node is indexed sequentially.
58
59
59
60
So the first thing to recall is that a very common way to represent a binary heap is with an array since every node can be indexed sequentially.
60
61
61
-
So if we were to represent the following binary heap in an array we would gets this array of values. <pause>
62
+
So if we were to represent the following binary heap in an array, we would gets this array of values. <pause>
62
63
63
-
If we know the index of a node i we can figure out what its left and right child nodes are using simple formulas. The left child is 2 times i plus 1 and the right child is 2 times i plus 2 assuming indexes are zero based.
64
+
If we know the index of a node i, we can figure out what its left and right child nodes are using simple formulas. The left child is 2 times i plus 1 and the right child is 2 times i plus 2 assuming indexes are zero based.
64
65
65
66
For example, what are children of the node at index 4?
66
67
67
-
Well we can just plug i into the two formulas I just gave you to obtain 9 and 10. Of course, you can also run the math backwards to figure out what the parent of a given node is. This is especially useful if you're either walking up or down the tree.
68
+
Well, we can just plug i into the two formulas I just gave you to obtain indexes 9 and 10. Of course, you can also run the math backwards to figure out what the parent of a given node is. both of these are especially useful when you're either walking up or down the tree.
68
69
69
70
Whenever you want to insert a new value into the PQ you insert the new value at the insertion position at the bottom right of the binary tree.
70
71
71
-
Suppose we want to insert the value 8. This would violate the heap invariant, so we bubble/sift up the value until the invariant is met.
72
+
Suppose we want to insert the value 8. This would violate the heap invariant, so we bubble up the value until the invariant is met.
72
73
73
-
So swap nodes 5 and 12. The heap is still not satisfied so let's swap again.
74
+
So swap nodes 5 and 12. The heap invariant is still not satisfied so let's swap again.
74
75
75
76
Now swap nodes 2 and 5. Now the tree is balanced.
76
77
77
78
<press>
78
79
79
-
Now let's review how removals are done. In a traditional PQ, to remove items, search for the element you want to remove and then swap with last node, perform removal and finally bubble up or down the swapped value.
80
+
Now let's review how removals are done. In a traditional PQ, to remove items, search for the element you want to remove and then swap with the last node, perform the removal and finally bubble up or down the swapped value.
80
81
81
82
For this example, suppose we want to remove node the node with value 5.
82
83
83
-
We don't know where the node with value five is within our PQ so we have to search for it, this is one of the major differences between a PQ and an IPQ.
84
+
We don't know where the node with value 5 is within our PQ, so we have to search for it, this is one of the major differences between a PQ and an IPQ.
84
85
85
-
So start at node 0 and walk down the tree until a node with value five is found if any.
86
+
So start at node 0 and process each node sequentially until a node with value 5 is found, if any.
86
87
87
88
...
88
89
89
-
So we found the a node with a value of 5 so to actually remove it from the heap swap it with the rightmost bottom node.
90
+
So we found a node with a value of 5, so to actually remove it from the heap swap it with the rightmost bottom node.
90
91
91
92
Once that is done, remove node 5 from the tree.
92
93
93
-
Now the purple node we swapped may not satisfy the heap invariant so we need to either move it up or down the tree. In this case, since the purple node has a value of 1 which is smaller than its children and we're dealing with a max heap we'll want to move the node down.
94
+
Now the purple node we swapped into 5's position may not satisfy the heap invariant, so we need to either move it up or down the tree. In this case, since the purple node has a value of 1 which is smaller than its children and we're dealing with a max heap we'll want to move the node down.
94
95
95
96
<press>
96
97
97
98
So that was a quick recap of just about everything you need to know about the traditional PQ, now let's talk about implementing an IPQ with a binary heap.
98
99
99
100
---------------------------
100
101
101
-
For the following examples suppose we're dealing with N people with different priorities we need to serve. Perhaps because we're prioritizing people for a queue at a hospital or at a restaurant, who knows. The main thing is that we'll assume the values can dynamically change and that we always want to serve the person with the lowest priority.
102
+
For the following examples, suppose we're dealing with N people with different priorities we need to serve. Perhaps because we're prioritizing people for a queue at a hospital, a waiting line at a restaurant, or who knows what. The main thing is that we'll assume the values can dynamically change and that we always want to serve the person with the lowest priority.
102
103
103
-
To figure out who to serve next use a Min IPQ to sort by lowest value first.
104
+
To figure out who to serve next we'll use a Min IPQ to sort by lowest value first.
104
105
105
106
The first step as mentioned before is to assign each person a unique index value between [0, N), these are the key-index values in the second column beside each person's name.
106
107
107
-
Then i'm going to give each person initial values to place inside IPQ. These will be maintained by the IPQ once inserted. Note that values can be any comparable value not only integers as shown here. This means we can have strings, objects or whatever type of data as the value, but for this example integers is a lot easier to visualize.
108
+
Then i'm going to give each person initial values to place inside IPQ. These will be maintained by the IPQ once inserted. Note that values can be any comparable value not only integers as shown here. This means we can have strings, objects or whatever type of data as a value, but for this example integers is a lot easier to visualize.
108
109
109
-
If I was to build a min indexed priority queue out of the key-value pairs I had on the last slide this is the heap I would get. Remember that unlike the previous example we're sorting by smallest value first since we're dealing with a min heap, this is just to switch things up. I also believe min heaps are more common than max heaps in practice but don't quote me on that.
110
+
If I was to build a min indexed priority queue out of the key-value pairs I had on the last slide this is the heap I would get. Remember that unlike the previous example we're sorting by smallest value first since we're dealing with a min heap, I did this just to switch things up. I also believe that in general, min heaps are more common than max heaps.
110
111
111
112
If we want to access the value for any given key k, first you need to find its key index 'ki' and do a lookup in the vals array maintained by the IPQ.
112
113
@@ -118,7 +119,7 @@ Then index into the values array at index 1 to find the corresponding value for
118
119
119
120
Awesome, now we know how to get the value for any particular key in the IPQ, but now we might wonder how to get the index of the node for a particular key.
120
121
121
-
To do that we need to maintain some additional information, namely a Position Map we can use to tell us the index of a node in the heap for any given key-index 'ki'. For convenience I will store the Position Map as an array called 'pm' inside the IPQ.
122
+
To do that, we need to maintain some additional information, namely a Position Map we can use to tell us the index of a node in the heap for any given key-index 'ki'. For convenience I will store the Position Map as an array called 'pm' inside the IPQ.
122
123
123
124
As an example, let's find out which node represents the key "Dylan"
124
125
@@ -130,22 +131,22 @@ Here's a follow up question, where is "George" in the heap? I'll give you a quic
130
131
131
132
Alright, so with just about every operation, the first step is to find the key-index for the key we care about which is George.
132
133
133
-
Then we can use the key-index to do a lookup inside the Position Map and find that the node for George is Node 1.
134
+
Then we can use the key-index to do a lookup inside the Position Map and find that the node for George, which happens to be Node 1.
134
135
135
136
Great, now we know how to lookup the node for a given key, but how do we find the key for a given node? This inverse lookup will prove to be a useful operation; say you want to know which key is associated with the root node at index 0, well you need to be able to do an inverse lookup to figure that out.
136
137
137
-
To maintain the inverse lookup for nodes to keys we'll need to maintain an inverse lookup table I will denote as 'im' short for Inverse Map.
138
+
To maintain the inverse lookup for nodes to keys, we'll need to maintain an inverse lookup table. I will denote this lookup table as 'im' short for Inverse Map.
138
139
139
140
Let's see if we can figure out which person is represented in the node at index 2.
140
141
141
142
To do that, simply do a lookup in the inverse map at index 2.
142
143
143
-
That give us information about which key-index is associated with that node.
144
+
That gives us information about which key-index is associated with that node.
144
145
145
146
Knowing the key-index enables us to retrieve the actual key by doing a lookup in our bidirectional hashtable. In this case the node at position 2 represents the key "Anna".
146
147
<pause>
147
148
148
-
Followup question to make sure you're still paying attention, which key is being represented in the node at index position 3?
149
+
Followup question, to make sure you're still paying attention, which key is being represented in the node at index position 3?
149
150
150
151
Same as before, find the key-index through the Inverse Map.
151
152
@@ -155,9 +156,9 @@ We can then conclude that the node at position 3 represents the key "Isaac"
155
156
156
157
---------------------------------
157
158
158
-
What we just covered was simply how an indexed PQ is structured internally and what all the moving parts are, now we want to actually do some useful operations with this IPQ such as inserting new key-value pairs, removing key-value pairs based on key, and also updating the value associated with a key. These are all possible and actually very similar to how you would do it in a regular PQ.
159
+
What we just covered was how an indexed PQ is structured internally, and what all the moving parts are, now we want to actually do some useful operations with this IPQ, such as inserting new key-value pairs, removing key-value pairs based on key, and also updating the value associated with a key. These are all possible and actually very similar to how you would do it in a regular PQ.
159
160
160
-
Insertion is nearly the same except that we have to update the Position Map 'pm' and the Inverse Map 'im' indexes to reflect the movement of the key-value pairs. Suppose we want to insert the key "Mary" with a value of 2 into the IPQ. What we first have to do is assign Mary a unique key-index value. Then we're going to insert the new keyvalue pair at the insertion position.
161
+
Insertion is nearly the same except that we have to update the Position Map 'pm' and the Inverse Map 'im' to reflect the movement of the key-value pairs. Suppose we want to insert the key "Mary" with a value of 2 into the IPQ. What we first have to do is assign Mary a unique key-index value. Then we're going to insert the new key-value pair at the insertion position.
161
162
162
163
Notice that upon insertion we updated all our arrays at index 12 to reflect that a new value was inserted.
163
164
@@ -281,4 +282,4 @@ Well that's all I got for you on the IPQ, let me know if you have any questions,
281
282
282
283
Thanks for watching, I hope you learned something. Please like this video if you learned something and subscribe for more mathematics and computer science videos.
0 commit comments