Outline
- linked list implementation of stack ADT
- nodes
- linked lists
- stack applications
Reminder: stack ADT
- void push(String value) adds the value to the top of the stack
- String pop() throws EmptyStackException removes the
value on top of the stack and returns it (or throws the exception)
- boolean empty() reports whether the stack is empty
- String peek() throws EmptyStackException
- notice this is now a
stack of Strings
array implementation, linked implementation
- in the array implementation, had to use a fixed-size structure,
an array, to store a variable amount of data
- this makes things complicated, e.g. when having to create a new,
bigger array
- to really do this well, we might also want to use a smaller array
when the stack becomes small
- instead of a single structure that we try to make the right size,
we could use a variable number of fixed-size objects, which we call
nodes
- each node has a field that refers to (points to) the next node, if
any
- the last node has a null reference (pointer) to the next node
- the result is a linked list of nodes
Node class
public class Node {
private String item;
private Node next;
/* methods */
}
- the first data item references the string that this node keeps track of
- the second item references the next node, if any -- this may be
null
- some useful methods:
- public Node(String value, Node next), the constructor
- public String getItem() and
public String getNext(), the two
accessor methods
- public void setNext(Node next), the only mutator method
- see also the
complete implementation
In-class exercise
On your own, draw a diagram of the result of this code:
Node node1 = new Node("string1", null);
Node node2 = new Node("string2", null);
Node node3 = new Node("string3", null);
Node node4 = new Node("string4", null);
Node node5 = new Node("string5", null);
node1.setNext(node5);
node2.setNext(node4);
node3.setNext(node2);
node4.setNext(null);
node5.setNext(node3);
Linked list stack implementation
- the stack is implemented with a single reference to a Node
- if the reference is null, the stack is empty
- if the reference points to a node, that node's item is the
top element of the stack
- the remainder of the list contains the remainder of the stack
- this is a recursive definition!
public class StringLinkedStack implements StringStackInterface {
private Node top;
/* methods */
public StringLinkedStack() {
top = null;
}
}
- two methods are particularly interesting: push and pop
-
public void push(String item) {
Node newTop = new Node(item, top);
top = newTop;
}
or, even simpler,
public void push(String item) {
top = new Node(item, top);
}
-
public String pop() {
if (top == null) {
throw new EmptyStackException();
}
String value = top.getItem();
top = top.getNext();
return value;
}
- see also the
complete implementation
Linked list stack performance
- in-class exercise: what is the run-time performance of push?
- in-class exercise: what is the run-time performance of pop?
Java notes
- top is a reference to a Node, and is not itself
a node
- likewise next in the Node class
-
pointer tutorial
Linked lists
- linked lists can be used for much more than stacks
- each node has a field to refer to the next node, and also the
local data for the node
- the above list could be for a stack with four elements, but could
be used for other purposes as well
Object usage
- a static method is called independently of any objects
- non-static methods must be called by reference to an object,
e.g.
top.getNext()
- what happens if top is null?
- NullPointerException!
Stack usage: balanced parentheses
- balanced parenteses: "(a (b c) [d (e)] f g)"
- unbalanced parenteses: "(a (b c {d (e)) f g]"
- algorithm to check for balanced parentheses:
- when encountering an open parentheses, put it on the stack
- when encountering a closed parentheses, remove the matching
one from the top of the stack, or declare an error
- at the end of the string, should have an empty stack
In-class exercise
everyone together, figure out what
this code does
Stack usage: infix to postfix conversion
- an infix expression must use parentheses sometimes to overcome
operator precedence and associativity
- for example, a * (b + c) requires parentheses
- the postfix form needs no parentheses: a b c + *
- the prefix form also needs no parentheses: * a + b c
- algorithm for converting infix to postfix:
- start with a fully parenthesized infix expression, e.g. (a * (b + c))
- move from left to right
- output each operand as it is read
- push each operator onto a stack
- push each left parentheses onto a stack
- when encountering a right parenthesis, pop and output operators,
until finding a matching left parenthesis
- for example, when processing (a * (b + c)), we get:
output | remaining string | stack |
_ | (a * (b + c)) | ( |
_ | a * (b + c)) | ( |
a | * (b + c)) | ( |
a | (b + c)) | ( * |
a | b + c)) | ( * ( |
a b | + c)) | ( * ( |
a b | c)) | ( * ( + |
a b c | )) | ( * ( + |
a b c + | ) | ( * |
a b c + * | _ | _ |
- this correctly adds b and c first, then multiplies them by a
- always works on strings that represent valid infix expressions
- algorithm in handout a little more elaborate, takes into
account precedence of infix operators
In-class exercise
- in groups or alone, use the algorithm from the handout to
convert the following expressions from infix to postfix:
- --- 5 + 5 / 3
- --- 5 / 5 + 3
- --- 5 * 5 * 3
- --- (5 + 5) / 3
- --- ((5 + 4 - 2 / 3) * 2 + (7 * 2 - 8)) % 3