Lecture 27 - Priority Queues


As usual, create a directory to hold today's activities:

$ mkdir ~/cs170/labs/lab27
$ cd ~/cs170/labs/lab27

Priority Queues

Earlier this week, we discussed Queues. Queues are FIFO data structures: the next item removed from the queue is the oldest item in the queue. Queues are incredibly useful data structures, but sometimes we don't necessarily want to access data based on the original order we added them to the Queue. Sometimes we want to retrieve the data out of the queue based on their priority. That's what a priority queue gives us.


Lab Activity 1
Priority Queue Class

A Priority Queue is generalized form of a Queue. A queue simply removes items from the queue based on time. In a priority queue, we can specify a priority value, so that items are removed in the correct order.

Details

In a file called priority_queue.py, create a class called PriorityQueue. This class must implement the four main methods of a Priority Queue:

You might also want a function for size of your queue as well. You should implement the above functions using a LinkedList, as opposed to an actual python list.

Example

  >>> pq = PriorityQueue()
  >>> pq.insert(1, 1)
  >>> pq.insert(0, 0)
  >>> pq.insert(3, 3)
  >>> pq.insert(2, 2)
  >>> print(pq)
  head -> (0, 0) -> (1, 1) -> (2, 2) -> (3, 3) -> None
  >>> print(pq.dequeue_max())
  0
  >>> print(pq.dequeue_max())
  1
  >>> print(pq.dequeue_max())
  2
  >>> print(pq.dequeue_max())
  3
  >>> pq.insert("Hello", 5)
  >>> pq.insert("World", 6)
  >>> pq.insert("!", 2)
  >>> print(pq)
  head -> ("!", 2) -> ("Hello", 5) -> ("World", 6) -> None
  >>> pq.update_priority("!", 7)
  >>> print(pq)
  head -> ("Hello", 5) -> ("World", 6) -> ("!", 7) -> None

Hint

  • You can create a tuple by separating the values using a comma:

          >>> my_tuple = (2, 3)
          >>> print(my_tuple)
          (2, 3)
          

  • Your insert method is going to look a lot like the insert procedure for insertion sort. This time, however, you are inserting into a linked list.

  • update_priority could just resort the list. However, we are just going to delete the current node, and re-insert it into the correct place.

 

Challenge

As of right now, insert is a O(n) operation, while dequeue is a O(1) operation. However, sometimes you are going to have a queue where you perform a lot of insertions, but only a few dequeues. Create a copy of the above class so that the insert operation is O(1), at the expense of a O(n) dequeue.

How would you describe the difference between how these classes operate? Do they now behave like two well known algorithms we've discussed in class?


Notes

priority_queue.py
Lab Assignment 27
A* Search

Searching using a standard queue is guaranteed to find the end location, given a path exists, in O(n). However, for large mazes that might be extremely slow. Our goal should be to advance to the end location as quickly as possible. Instead of prioritizing expanding of cell locations by time added to the queue, we can prioritize expansion of cell based on their distance from the goal location!

Details

Copy your maze.py file from Monday's lab into today's lab directory:

$ cd ~cs170/labs/lab27
$ cp ../lab25/maze.py .

You are going to make two adjustments to this file. First, you are going to read in a text file containing a maze. Write a function read_file(file_name), which takes a string which refers to a file you have permission to read from. It should return a 2-dimensional python list of the elements of the list. You should read the name of the file using an input statement, and call the above function to get the 2-d list of the maze. The input file will be formatted as 50 lines of 50 characters each. a 0 represents an open location, and a 1 represents a wall. You can assume that all of the mazes you read are 50 × 50, where the start cell is always (0, 0) and the end cell is always (48, 48). You will use this read in maze to test the A* algorithm.

A* works almost exactly the same as the search we did on Monday. However, instead of using a normal queue, it uses a priority queue. The priority for a cell is based on its euclidean distance to the end cell.

Edit your can_traverse method of the Maze class, so that it uses a priority queue instead of the usual queue. You might want to write a euclidean_distance function in your file, to compute the appropriate priorities.

The file mazes.tgz contains several mazes you can test your program on. For these mazes, there is always a path to the end cell. You should make sure your program identifies that a path exists for every file.

Hint

This exercise should be very straight forward, assuming you have written your priority queue correctly. You will still just be pulling the item from the front of the queue to expand. The only difference is that when you insert an item into the queue, you need the priority of that queue to be the euclidean distance to that node.

 

Challenge

Determining if there exists a path is very straight forward. However, solving a maze that we know there is a path is not terribly interesting. What we really would like is the actual path that would get us to the end location. Create a copy of the can_traverse method that returns a list of tuples, the cells that must be traversed in order to get to the end cell of the maze.


Submission

When you have finished, create a tar file of your lab27 directory. To create a tar file, execute the following commands:

cd ~/cs170/labs
tar czvf lab27.tgz lab27/

To submit your activity, go to cseval.roanoke.edu. You should see an available assignment called Lab Assignment 27. Make sure you include a header listing the authors of the file.


Last modified: Fri Mar 28 11:16:45 EDT 2014