Skip to content

Commit ddfde98

Browse files
committed
IPQ
1 parent 64b6524 commit ddfde98

2 files changed

Lines changed: 43 additions & 42 deletions

File tree

-10.3 KB
Binary file not shown.

slides/priorityqueue/indexed_priority_queue.txt

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff 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.
23

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.
45

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.
67

78
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:
89
- 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
1213
- Richard fractured his wrist so priority of 5
1314
- and lastly Leah's stomach also hurts
1415

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.
1617

1718
Followed by James.
1819

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
2021

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.
2223

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.
2425

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.
2627

2728
Once Naida is dealt with, Akarsh is next.
2829

2930
Followed by Leah.
3031

3132
<press>
3233

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.
3536

3637
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.
3738

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.
4041

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:
4243
- deleting keys,
4344
- getting the value associated with a key
4445
- 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
4748
- 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.
5051

51-
An IPQ can be implemented in several ways with 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.
5253

5354
---------------------------
5455

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.
5657

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.
5859

5960
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.
6061

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>
6263

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.
6465

6566
For example, what are children of the node at index 4?
6667

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.
6869

6970
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.
7071

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.
7273

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.
7475

7576
Now swap nodes 2 and 5. Now the tree is balanced.
7677

7778
<press>
7879

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.
8081

8182
For this example, suppose we want to remove node the node with value 5.
8283

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.
8485

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.
8687

8788
...
8889

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.
9091

9192
Once that is done, remove node 5 from the tree.
9293

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.
9495

9596
<press>
9697

9798
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.
9899

99100
---------------------------
100101

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.
102103

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.
104105

105106
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.
106107

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.
108109

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.
110111

111112
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.
112113

@@ -118,7 +119,7 @@ Then index into the values array at index 1 to find the corresponding value for
118119

119120
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.
120121

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.
122123

123124
As an example, let's find out which node represents the key "Dylan"
124125

@@ -130,22 +131,22 @@ Here's a follow up question, where is "George" in the heap? I'll give you a quic
130131

131132
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.
132133

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.
134135

135136
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.
136137

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.
138139

139140
Let's see if we can figure out which person is represented in the node at index 2.
140141

141142
To do that, simply do a lookup in the inverse map at index 2.
142143

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.
144145

145146
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".
146147
<pause>
147148

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?
149150

150151
Same as before, find the key-index through the Inverse Map.
151152

@@ -155,9 +156,9 @@ We can then conclude that the node at position 3 represents the key "Isaac"
155156

156157
---------------------------------
157158

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.
159160

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 key value 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.
161162

162163
Notice that upon insertion we updated all our arrays at index 12 to reflect that a new value was inserted.
163164

@@ -281,4 +282,4 @@ Well that's all I got for you on the IPQ, let me know if you have any questions,
281282

282283
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.
283284

284-
285+
[outro sound]

0 commit comments

Comments
 (0)