You are on page 1of 37

STACKS AND QUEUES

‣ stacks
‣ resizing arrays
‣ queues
‣ generics

h t t p : / / a l g s 4. c s . p r i n c e t o n . e d u
Stack API

Warmup API. Stack of strings data type.

push pop

public class StackOfStrings

StackOfStrings() create an empty stack

void push(String item) insert a new string onto stack

remove and return the string


String pop()
most recently added

boolean isEmpty() is the stack empty?

int size() number of strings on the stack

Warmup: Reverse sequence of strings from standard input.

2
Sample client

Warmup client. Reverse sequence of strings from standard input.


・Read string and push onto stack.
・Pop string and print. push pop

public class ReverseStrings


{
public static void main(String[] args)
{
StackOfStrings stack = new StackOfStrings();
while (!StdIn.isEmpty())
stack.push(StdIn.readString());
while (!stack.isEmpty())
StdOut.println(stack.pop());
} % more tinyTale.txt
} it was the best of times ...

% java ReverseStrings < tinyTale.txt


... times of best the was it
[ignoring newlines] 3
Stack test client

Read strings from standard input.


・If string equals "-", pop string from stack and print.
・Otherwise, push string onto stack. push pop

public static void main(String[] args)


{
StackOfStrings stack = new StackOfStrings();
while (!StdIn.isEmpty())
{
String s = StdIn.readString();
if (s.equals("-")) StdOut.print(stack.pop());
else stack.push(s);
}
}
% more tobe.txt
to be or not to - be - -

% java StackOfStrings < tobe.txt


to be not
4
Fixed-capacity stack: array implementation

・Use array s[] to store N items on stack.


・push(): add new item at s[N].
・pop(): remove item from s[N-1].

top of stack

s[] it was the best of times null null null null

0 1 2 3 4 5 6 7 8 9

Defect. Stack overflows when N exceeds capacity. [stay tuned]

5
Fixed-capacity stack: array implementation

public class FixedCapacityStackOfStrings


{
private String[] s; a cheat
private int N = 0; (stay tuned)

public FixedCapacityStackOfStrings(int capacity)


{ s = new String[capacity]; }

public boolean isEmpty()


{ return N == 0; }

public void push(String item)


{ s[N++] = item; }

public String pop()


use to index into array; { return s[--N]; }
then increment N }

decrement N;
then use to index into array

6
Stack considerations

Overflow and underflow.


・Underflow: throw exception if pop from an empty stack.
・Overflow: use resizing array for array implementation. [stay tuned]
Null items. We allow null items to be inserted.

Loitering. Holding a reference to an object when it is no longer needed.

public String pop() public String pop()


{ return s[--N]; } {
String item = s[--N];
loitering
s[N] = null;
return item;
}
this version avoids "loitering":
garbage collector can reclaim memory for an
object only if no outstanding references

7
STACKS AND QUEUES
‣ stacks
‣ resizing arrays
‣ queues
‣ generics

h t t p : / / a l g s 4. c s . p r i n c e t o n . e d u
Stack: resizing-array implementation

Problem. Requiring client to provide capacity does not implement API!


Q. How to grow and shrink array?

First try.
・push(): increase size of array s[] by 1.
・pop(): decrease size of array s[] by 1.

Too expensive. infeasible for large N

・Need to copy all items to a new array, for each operation.


・Array accesses to insert first N items = N + (2 + 4 + … + 2(N – 1)) ~ N 2.

1 array access 2(k–1) array accesses to expand to size k


per push (ignoring cost to create new array)

Challenge. Ensure that array resizing happens infrequently.


9
Stack: resizing-array implementation

"repeated doubling"
Q. How to grow array?
A. If array is full, create a new array of twice the size, and copy items.

public ResizingArrayStackOfStrings()
{ s = new String[1]; }

public void push(String item)


{
if (N == s.length) resize(2 * s.length);
s[N++] = item;
}

private void resize(int capacity)


{
String[] copy = new String[capacity];
for (int i = 0; i < N; i++)
copy[i] = s[i];
s = copy;
}

Array accesses to insert first N = 2i items. N + (2 + 4 + 8 + … + N) ~ 3N.

1 array access k array accesses to double to size k


per push (ignoring cost to create new array) 10
Stack: resizing-array implementation

Q. How to shrink array?

First try.
・push(): double size of array s[] when array is full.
・pop(): halve size of array s[] when array is one-half full.

Too expensive in worst case.


・Consider push-pop-push-pop-… sequence when array is full.
・Each operation takes time proportional to N.

N = 5 to be or not to null null null

N = 4 to be or not

N = 5 to be or not to null null null

N = 4 to be or not

12
Stack: resizing-array implementation

Q. How to shrink array?

Efficient solution.
・push(): double size of array s[] when array is full.
・pop(): halve size of array s[] when array is one-quarter full.

public String pop()


{
String item = s[--N];
s[N] = null;
if (N > 0 && N == s.length/4) resize(s.length/2);
return item;
}

Invariant. Array is between 25% and 100% full.


13
Stack: resizing-array implementation trace

Remark. Many unused spaces (null)

14
Stack: linked-list implementation

・Maintain pointer first to first node in a singly-linked list.


・Push new item before first.
・Pop item from first.

top of stack

of best the was it null

first

18
Stack: linked-list representation

Maintain pointer to first node in a linked list; insert/remove from front.

push("to")

push("be") first

insert at front
push("or")
of linked list

push("not")

push("to")

pop()
remove from front
of linked list
push("be")

pop()

pop()
19
Stack pop: linked-list implementation

inner class

private class Node


{
String item;
Node next;
}

20
Stack push: linked-list implementation

inner class

private class Node


{
String item;
Node next;
}

21
Stack: linked-list implementation in Java

public class LinkedStackOfStrings


{
private Node first = null;

private class Node


{
String item; private inner class
Node next; (access modifiers for instance
}
variables don't matter)
public boolean isEmpty()
{ return first == null; }

public void push(String item)


{
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
}

public String pop()


{
String item = first.item;
first = first.next;
return item;
}
}

22
Stack: linked-list implementation performance

Proposition. Every operation takes constant time in the worst case.

Proposition. A stack with N items uses ~ 40 N bytes.

inner class 16 bytes (object overhead)

private class Node


8 bytes (inner class extra overhead)
{
String item; 8 bytes (reference to String)

Node next; 8 bytes (reference to Node)


}
40 bytes per stack node

Remark. This accounts for the memory for the stack


(but not the memory for strings themselves, which the client owns).

23
Stack implementations: resizing array vs. linked list

Tradeoffs. Can implement a stack with either resizing array or linked list;
client can use interchangeably. Which one is better?

Linked-list implementation.
・Every operation takes constant time in the worst case.
・Uses extra time and space to deal with the links.
Resizing-array implementation.
・Every operation takes constant amortized time.
・Less wasted space.

N = 4 to be or not null null null null

24
1.3 B AGS, Q UEUES, AND
STACKS
‣ stacks
‣ resizing arrays
‣ queues
‣ generics

h t t p : / / a l g s 4. c s . p r i n c e t o n . e d u
Queue API

enqueue

public class QueueOfStrings

QueueOfStrings() create an empty queue

void enqueue(String item) insert a new string onto queue

remove and return the string


String dequeue()
least recently added

boolean isEmpty() is the queue empty?

int size() number of strings on the queue

dequeue

26
Queue test client

Read strings from standard input.


・If string equals "-", dequeue string and print.
・Otherwise, enqueue string.
public static void main(String[] args)
{
QueueOfStrings q = new QueueOfStrings();
while (!StdIn.isEmpty())
{
String s = StdIn.readString();
if (s.equals("-")) StdOut.print(q.dequeue());
else q.enqueue(s);
}
}
% more tobe.txt
to be or not to - be - - that - - - is

% java QueueOfStrings < tobe.txt


to be or not to be
27
Queue: linked-list implementation

・Maintain one pointer first to first node in a singly-linked list.


・Maintain another pointer last to last node.
・Dequeue from first.
・Enqueue after last.

front of queue back of queue

it was the best of times null

first last

28
Queue: linked-list representation

Maintain pointer to first and last nodes in a linked list;


remove from front; insert at end.

enqueue("to")

enqueue("be")

insert at end
enqueue("or")
of linked list

enqueue("not")

enqueue("to")

dequeue()
remove from front
of linked list enqueue("be")

dequeue()
first

dequeue()
last

29
Queue dequeue: linked-list implementation

inner class

private class Node


{
String item;
Node next;
}

Remark. Identical code to linked-list stack pop().


30
Queue enqueue: linked-list implementation

inner class

private class Node


{
String item;
Node next;
}

31
Queue: linked-list implementation in Java

public class LinkedQueueOfStrings


{
private Node first, last;

private class Node


{ /* same as in LinkedStackOfStrings */ }

public boolean isEmpty()


{ return first == null; }

public void enqueue(String item)


{
Node oldlast = last;
last = new Node();
last.item = item;
last.next = null; special cases for
if (isEmpty()) first = last;
empty queue
else oldlast.next = last;
}

public String dequeue()


{
String item = first.item;
first = first.next;
if (isEmpty()) last = null;
return item;
}
}
32
Queue: resizing-array implementation

・Use array q[] to store items in queue.


・enqueue(): add new item at q[tail].
・dequeue(): remove item from q[head].
・Update head and tail modulo the capacity.
・Add resizing array.

front of queue back of queue

q[] null null the best of times null null null null

0 1 2 3 4 5 6 7 8 9

head tail capacity = 10

Q. How to resize?
33
1.3 B AGS, Q UEUES, AND
STACKS
‣ stacks
‣ resizing arrays
‣ queues
‣ generics

h t t p : / / a l g s 4. c s . p r i n c e t o n . e d u
Parameterized stack

We implemented: StackOfStrings.
We also want: StackOfURLs, StackOfInts, StackOfVans, ….

Attempt 1. Implement a separate stack class for each type.


・Rewriting code is tedious and error-prone.
・Maintaining cut-and-pasted code is tedious and error-prone.

This was the most reasonable approach until Java 1.5.

35
Parameterized stack

We implemented: StackOfStrings.
We also want: StackOfURLs, StackOfInts, StackOfVans, ….

Attempt 2. Implement a stack with items of type Object.


・Casting is required in client.
・Casting is error-prone: run-time error if types mismatch.

StackOfObjects s = new StackOfObjects();


Apple a = new Apple();
Orange b = new Orange();
s.push(a);
s.push(b);
a = (Apple) (s.pop());

run-time error
36
Parameterized stack

We implemented: StackOfStrings.
We also want: StackOfURLs, StackOfInts, StackOfVans, ….

Attempt 3. Java generics.


・Avoid casting in client.
・Discover type mismatch errors at compile-time instead of run-time.
type parameter

Stack<Apple> s = new Stack<Apple>();


Apple a = new Apple();
Orange b = new Orange();
s.push(a);
s.push(b); compile-time error
a = s.pop();

Guiding principles. Welcome compile-time errors; avoid run-time errors.


37
Generic stack: linked-list implementation

public class LinkedStackOfStrings public class Stack<Item>


{ {
private Node first = null; private Node first = null;

private class Node private class Node


{ { generic type name
String item; Item item;
Node next; Node next;
} }

public boolean isEmpty() public boolean isEmpty()


{ return first == null; } { return first == null; }

public void push(String item) public void push(Item item)


{ {
Node oldfirst = first; Node oldfirst = first;
first = new Node(); first = new Node();
first.item = item; first.item = item;
first.next = oldfirst; first.next = oldfirst;
} }

public String pop() public Item pop()


{ {
String item = first.item; Item item = first.item;
first = first.next; first = first.next;
return item; return item;
} }
} }

38
Generic stack: array implementation

the way it should be

public class FixedCapacityStackOfStrings public class FixedCapacityStack<Item>


{ {
private String[] s; private Item[] s;
private int N = 0; private int N = 0;

public StackOfStrings(int capacity) public FixedCapacityStack(int capacity)


{ s = new String[capacity]; } { s = new Item[capacity]; }

public boolean isEmpty() public boolean isEmpty()


{ return N == 0; } { return N == 0; }

public void push(String item) public void push(Item item)


{ s[N++] = item; } { s[N++] = item; }

public String pop() public Item pop()


{ return s[--N]; } { return s[--N]; }
} }

@#$*! generic array creation not allowed in Java


39
Generic stack: array implementation

the way it is

public class FixedCapacityStackOfStrings public class FixedCapacityStack<Item>


{ {
private String[] s; private Item[] s;
private int N = 0; private int N = 0;

public ..StackOfStrings(int capacity) public FixedCapacityStack(int capacity)


{ s = new String[capacity]; } { s = (Item[]) new Object[capacity]; }

public boolean isEmpty() public boolean isEmpty()


{ return N == 0; } { return N == 0; }

public void push(String item) public void push(Item item)


{ s[N++] = item; } { s[N++] = item; }

public String pop() public Item pop()


{ return s[--N]; } { return s[--N]; }
} }

the ugly cast


40
Generic data types: autoboxing

Q. What to do about primitive types?

Wrapper type.
・Each primitive type has a wrapper object type.
・Ex: Integer is wrapper type for int.

Autoboxing. Automatic cast between a primitive type and its wrapper.

Stack<Integer> s = new Stack<Integer>();


s.push(17); // s.push(Integer.valueOf(17));
int a = s.pop(); // int a = s.pop().intValue();

Bottom line. Client code can use generic stack for any type of data.
44

You might also like