Link
Stacks and Queues
The Design Decision Hierarchy: choosing the right ADT, evaluating data structures, and reasoning about invariants.
Kevin Lin, with thanks to many others.
1
Ask questions anonymously on Piazza. Look for the pinned Lecture Questions thread.

Feedback from the Reading Quiz
2
The reading discussed representation invariants and gave a brief introduction to the Stack and Queue ADTs.

Implementer’s Design Decision Hierarchy
3
List
Abstract Data Type
Which ADT is the best fit?

Data Structure
Which data structure offers the best performance for our input/workload?

Implementation Details
How do we maintain invariants?
Resizable Array
Linked Nodes
data is an array of items, never null. The i-th item in the list is always stored in data[i].
?: How do we determine whether one data structure is faster than another? Does it depend on the implementation details?




?: How do invariants relate to data structures?

Which List implementation is faster for removeFront?
4

Big-O Runtime Analysis
What does it mean for a data structure to be slow or fast?
Big-O runtime analysis: count how many steps a program takes to execute an input of size N.
Suppose our list has N items.
A method that takes a constant number of steps (e.g. 23) is in O(1).
A method that takes a linear number of steps (e.g. 4N + 3) is in O(N).
5
Number of steps (runtime)
Size of input (N)
O(N)
O(1)
?: How does constant or linear relate to analyzing runtime “with respect to big inputs”?




?: What are the big-O runtimes for ArrayList and LinkedList removeFront?




?: Can we say that an ADT is slower or faster than another ADT?

ArrayList vs. LinkedList
Which List implementation should we use to store a list of songs in a playlist?

Which List implementation should we use to store the history of a bank customer’s transactions?

Which List implementation should we use to store the order of students waiting to speak to a TA at a tutoring center?
6
Q
Time needed to access the i-th item from a list of N items.
ArrayList: O(1)
LinkedList: O(N)

Time needed to insert an item at position i in a list of N items.
ArrayList: O(N)
LinkedList: O(N)

?: Why are these runtimes what they are?

Q1: Which List implementation should we use to store a list of songs in a playlist?




Q2: Which List implementation should we use to store the history of a bank customer’s transactions?




Q3: Which List implementation should we use to store the order of students waiting to speak to a TA at a tutoring center?

Which Stack implementation is faster overall?
7
Recall that the Stack ADT specifies two important methods:

push(Item item): Puts the item on the top of the stack.
Item pop(): Removes and returns the top item of the stack.

Assume for the resizable array that we use the addLast and removeLast methods from ArrayList. Assume for linked nodes that we use the addFirst and removeFirst methods from LinkedList, and we have a reference to the front of the LinkedList.

ArrayStack
8
Item[] data
int size
push – resize data array if necessary; assign data[size] = item; increment size
pop – return data[size]; decrement size
State
Behavior
0
1
2
3
push(3)
push(4)
pop()
push(5)
push
pop
3
3
4
3
4
3
5
push – O(1) if not resizing;O(N) if resizing
pop – O(1)
Runtime
0
size
1
2
1
2
?: How do the Stack ADT methods compare to List ADT methods?




?: How do the implementations for ArrayList methods differ from ArrayStack methods?

LinkedStack
9
Node top
int size
push – create a new node linked to top; update top to new node; increment size
pop – return top item; update top; decrement size
State
Behavior
push(3)
push(4)
pop()
push
pop
push – O(1) always
pop – O(1)
Runtime
0
size
1
2
1
top
3
4
?: If the push and pop operations of LinkedStack is always at least as good or better than ArrayStack, would we ever want to use ArrayStack?

ArrayList vs. LinkedList
Which List implementation should we use to store a list of songs in a playlist?

Which List implementation should we use to store the history of a bank customer’s transactions?

Which List implementation should we use to store the order of students waiting to speak to a TA at a tutoring center?
10
Queue

Hiding Program Complexity
Contract: Assuming they agree to the ADT’s possible values and operations, the client and the implementer can improve their programs at the same time.
Invariants: A checklist of assumptions the implementer needs to maintain every time they add a behavior to a data structure.
If the List ADT does everything the Stack and Queue ADTs can do, why use Stack or Queue instead of List?
11
Q
Implementer
Client
ADT
?: How do invariants affect the implementation of ArrayList and ArrayStack?




Q1: If the List ADT does everything the Stack and Queue ADTs can do, why use Stack or Queue instead of List?

ArrayQueue: Design 1
12
0
1
2
3
add
remove


Same design as ArrayStack: borrow ArrayList’s addLast and removeFront.
It’s basically just an ArrayList.

What are the runtimes for ArrayQueue (Design 1) add and remove?
13

Reconsidering Data Structure Invariants
ArrayQueue (Design 1) is basically just an ArrayList.
Recall the representation invariant for the underlying data array in an ArrayList.
data is an array of items, never null.The i-th item in the list is always stored in data[i].
How does maintaining this invariant affect the runtimes for add and remove?

Propose an invariant that could result in faster runtimes for add and remove.
14
Q
Q1: How does this invariant relate to the runtimes for add and remove?




Q2: Propose an invariant that could result in faster runtimes for add and remove.

The i-th item does not need to be data[i] so the front of the queue does not need to be the front of the array!
ArrayQueue: Design 2
15
0
1
2
3
add(3)
add(4)
remove()
add(5)
add(6)
add(7)
add
remove
3
3
4
3
4
3
4
5
0
size
1
2
1
2
A
front
back
0
0
1
2
1
3
3
3
4
5
6
0
1
Circular
7
4
5
6
4
Removing items increments front
front represents the index of the front of the queue (except when the queue is empty) while back represents the index for the next item.

?: What’s the runtime for ArrayQueue (Design 2) add and remove?




?: Is it necessary to maintain an integer index for remembering the back of the array?




?: We found a faster way to implement ArrayQueue. Is it possible to take these invariants and use them to implement a faster ArrayList?

1
1
The i-th item does not need to be data[i] so the front of the queue does not need to be the front of the array!


Give an invariant that describes this behavior in your own words.
ArrayQueue: Design 2
16
0
1
2
3
add
remove
size
Q
front
back
Circular
Removing items increments front
7
4
5
6
4
front represents the index of the front of the queue (except when the queue is empty) while back represents the index for the next item.

Q1: Give an invariant that describes this behavior in your own words.

Give an invariant that describes ArrayQueue (Design 2) in your own words.
17

LinkedQueue: Design 1
18
add
remove
back
3
4


Same design as LinkedStack: borrow LinkedList’s addLast and removeFront.
Which method has a worse runtime: add or remove?
How could we improve the runtime?
Q
Q1: Which method has a worse runtime: add or remove?




Q2: How would you improve the runtime?




?: How does this change your visualization of the data structure?

LinkedQueue: Design 2
19
add
remove
back
3
4




Add a front pointer.
front
A
?: What are other possible designs for LinkedQueue? What set of invariants can result in a slower LinkedQueue implementation?

Implementer’s Design Decision Hierarchy
20
List
Abstract Data Type
Which ADT is the best fit?

Data Structure
Which data structure offers the best performance for our input/workload?

Implementation Details
How do we maintain invariants?
Resizable Array
Linked Nodes
data is an array of items, never null. The i-th item in the list is always stored in data[i].
Today, we studied the ADT implementer’s view of the Design Decision Hierarchy. A recurring theme in computer science is that problem representations (implementation details) reflect problem solutions (data structures).

One neat observation: by simplifying the ADT interface, we gave the implementer more control over how they implemented their data structures. The more complex the ADT, the more restrictive the invariants, which means the implementer might not be able to make as many runtime optimizations.

?: We’ll later look at the ADT client’s perspective. How does the client determine which ADT is the best fit? To what extent does the client need to worry about ADT and data structure complexity?