19: ADT and Search, Reviewed

Announcements

Quiz in our next discussion (next Tuesday, which is a Monday schedule).

Twelve programming assignments are planned. The workload in this course is nothing if not consistent. Please don’t be mad at me because another instructor didn’t remember to tell you about an end-of-term paper until this week.

The first batch of ADH notices went out earlier today. More are almost certainly coming.

Specialized linear ADTs

The “standard” linear ADTs (in Java) are the array and the (generic) List. Arrays are a simple type, with very fast random access but the limitation of fixed sizes. Lists are more flexible, and their underlying implementation is generally written in terms of (resizing) arrays or (sometimes) in terms of a linked list.

But as we’ve mentioned, there are other linear data structures that one might use; they are similar to lists but restrict themselves in various ways. We’re going to revisit them so you’re ready when you see them again (“for the first time”) in 187. Today, we’ll focus on behavior, not implementation, but we will do toy implementations soon.

Stacks

Stacks are a last-in, first-out data structure. They are like lists, but instead of allowing for random access (via get, remove, add), they restrict a user to adding items on one end (the “top”) and removing from that same position. These operations are sometimes called push (add an item), pop (remove an item), and peek (look at but do not remove the top item).

Modern Java suggests we use the Deque interface, which is short for double-ended queue, and use the addFirst, removeFirst, and peekFirst methods. In either case, though, the behavior is the same, LIFO.

s.push("a");
s.push("b");
s.pop();
s.push("c");
s.push("d");
s.peek();

(top on right)

  • After the first operation, the stack contains [“a”]
  • After the second, the stack containes [“a”, “b”].
  • removes and returns “b”, then stack contains, [“a”]
  • [“a”, “c”]
  • [“a”, “c”, “d”]
  • peek returns “d”

In class exercise 1

s.push(1);
s.push(2);
s.push(3);
s.peek()
s.pop();
s.push(1);
s.pop();
s.pop();

What are the contents of the stack after this code executes?

  • [1]
  • [2, 1]
  • [3, 2, 1]
  • [3, 2, 1]
  • [2, 1]
  • [1, 2, 1]
  • [2, 1]
  • [1]

Queues

Queues are a first-in, first-out data structure. Java has a Queue interface you can use, or you can (also) use Deque, as described in its documentation. In a Queue, we typically talk about add (always at one end) remove (always from the other), and sometimes peek (just like a stack, returns but does not remove the next element that remove would return).

q.add("a");
q.add("b");
q.remove();
q.add("c");
q.add("d");
q.peek();
  • [“a”]
  • [“a”, “b”]
  • removes and returns “a”, queue contains [“b”]
  • [“b”, “c”]
  • [“b”, “c”, “d”]
  • returns “b”

In-class exercise 2

q.add(1);
q.add(2);
q.add(3);
q.peek()
q.remove();
q.add(1);
q.remove();
q.remove();

What are the contents of the queue after this code executes? (rear on right)

  • [1]
  • [1, 2]
  • [1, 2, 3]
  • [1, 2, 3]
  • [2, 3]
  • [2, 3, 1]
  • [3, 1]
  • [1]

A side note: over/underflow

Stacks and queues can underflow. If you call pop or remove on an empty stack/queue, this will generate an exception.

Some stacks and queues are bounded, which means they have an explicit capacity. If you try to push or add to a stack/queue that is already at capacity, then you will overflow the structure and generate an exception.

Priority queues

A priority queue is like a queue, but it returns the next “smallest” (in Java) thing, rather than the first-in thing, when remove or peek is called.

It’s important to note that the exact order of the items stored in the priority queue is not visible to the user; you can only see the “next” / “top” item (that will be returned by peek or remove). Internally, priority queues are implemented as “heaps”, which are a tree-based structure similar to, but different from, the binary search trees we talked about briefly earlier this semester. Heaps allow for efficient (log n) insertion and removal of the smallest item.

How do we define “smallest”? The usual way, by either depending upon the “natural” ordering of the elements stored in the PriorityQueue<E> (that is, they must implement Comparable) or by passing in a Comparator when constructing the PriorityQueue.

Suppose then we do the following with a priority queue:

pq.add("b");
pq.add("a");
pq.remove();
pq.add("c");
pq.add("d");
pq.peek();
  • [“b”]
  • [“b”, “a”]
  • removes and returns “a”, contents are [“b”]
  • [“c”, “b”]
  • [“c”, “d”, “b”] ; note we don’t know whether “c” or “d” comes first; all we know is “b” is up next to be removed
  • returns “b”

In class exercise 3

pq.add(3);
pq.add(2);
pq.add(1);
pq.peek()
pq.remove();
pq.add(1);
pq.remove();
pq.remove();
  • [3]
  • [3, 2]
  • [3, 2, 1] (but really we only know 1 is first)
  • returns 1
  • removes and returns 1, leaving [3, 2]
  • [3, 2, 1]
  • removes and returns 1, leaving [3, 2]
  • removes and returns 2, leaving [3]

Back to search

Now let’s do search, one more time, knowing what we know about stacks, queues, and priority queues.

Here’s the graph we’ll reference:

graph

This is similar to the homework graph but paths now have a cost on the edges (this is the g(x) from last class), and the vertices are labeled with their estimated cost to the goal, their heuristic value: (h(x)).

Assume that the neighbors of each vertex are returned in alphabetical order. For example, the only neighbors of S are A and B, in that order — S is a neighbor of C, but C is not a neighbor of S in this directed graph!

Suppose we execute our findPath method from last lecture with a Queue, so that we do a BFS. In what order are vertices added to the frontier?

(on board; see homework 18 solution)

Suppose we execute our findPath method from last lecture with a Stack, so that we do a DFS. What does our frontier look like?

(on board)

S
B A
G2 E A

Suppose we execute our findPath method from last lecture with a PriorityQueue, prioritized by h(x) values, so that we do a greedy least-cost search. What does our frontier look like?

(on board)

S
A B
D C B
G1 C B

If we wanted to do the “optimal” A* search, we’d have to order nodes in the priority queue by f(x) = h(x) + g(x). We’d also need to make sure that h(x) was consistent; more on this in later COMPSCI courses.