CH 04
CH 04
y
ka
k a y a k
yk
a
y
ka
a
yk
ya
k k a y a k
a
k private String buildReverse(){
StringBuilder result = new StringBuilder();
k while(!charStack.empty()) {
result.append(charStack.pop());
}
return result.toString();
}
Finding Palindromes (cont.)
...
0 1 2 3 4 5 6 7 8 9 10
( ( w * [ x + y ] / z )
balanced : true
index : 0
Balanced Parentheses (cont.)
Expression: (w * [x + y] / z)
0 1 2 3 4 5 6 7 8 9 10
( ( w * [ x + y ] / z )
balanced : true
index : 1
Balanced Parentheses (cont.)
Expression: (w * [x + y] / z)
0 1 2 3 4 5 6 7 8 9 10
( ( w * [ x + y ] / z )
balanced : true
index : 2
Balanced Parentheses (cont.)
Expression: (w * [x + y] / z)
0 1 2 3 4 5 6 7 8 9 10
(
[ ( w * [ x + y ] / z )
(
balanced : true
index : 3
Balanced Parentheses (cont.)
Expression: (w * [x + y] / z)
0 1 2 3 4 5 6 7 8 9 10
[ ( w * [ x + y ] / z )
(
balanced : true
index : 4
Balanced Parentheses (cont.)
Expression: (w * [x + y] / z)
0 1 2 3 4 5 6 7 8 9 10
[ ( w * [ x + y ] / z )
(
balanced : true
index : 5
Balanced Parentheses (cont.)
Expression: (w * [x + y] / z)
0 1 2 3 4 5 6 7 8 9 10
[ ( w * [ x + y ] / z )
(
balanced : true
index : 6
Balanced Parentheses (cont.)
Expression: (w * [x + y] / z)
0 1 2 3 4 5 6 7 8 9 10
(
[ ( w * [ x + y ] / z )
(
Matches!
Balanced still true
balanced : true
index : 7
Balanced Parentheses (cont.)
Expression: (w * [x + y] / z)
0 1 2 3 4 5 6 7 8 9 10
( ( w * [ x + y ] / z )
balanced : true
index : 8
Balanced Parentheses (cont.)
Expression: (w * [x + y] / z)
0 1 2 3 4 5 6 7 8 9 10
( ( w * [ x + y ] / z )
balanced : true
index : 9
Balanced Parentheses (cont.)
Expression: (w * [x + y] / z)
0 1 2 3 4 5 6 7 8 9 10
( ( w * [ x + y ] / z )
Matches!
Balanced still true
balanced : true
index : 10
Testing
Provide a variety of input expressions displaying the result
true or false
Try several levels of nested parentheses
Try nested parentheses where corresponding parentheses
are not of the same type
Try unbalanced parentheses
No parentheses at all!
@SupressWarnings("unchecked")
public ArrayStack() {
theData = (E[])new Object[INITIAL_CAPACITY];
} There is no size variable or method
Implementing a Stack Using an
Array (cont.)
Character
Object[]
value = 'J'
ArrayStack [0] = null
[1] = null
[2] = null Character
theData =
[3] = null
topOfStack = -1
3
0
1
2
[4] = null value = 'a'
[5] = null
[6] = null
[7] = null Character
[8] = null
[9] = null
value = 'v'
public E push(E obj) {
if (topOfStack == theData.length - 1){
Character
reallocate();
}
topOfStack++; value = 'a'
theData[topOfStack] = obj;
return obj;
}
Implementing a Stack Using an
Array (cont.)
@Override
public E pop() {
if (empty()) {
throw new EmptyStackException();
}
return theData[topOfStack--];
}
Implementing a Stack Using an
Array (cont.)
This implementation is O(1), in contrast to the Pez
analogy and the “kayak” example, which are both
O(n)
Implementing a Stack as a Linked
Data Structure
We can also implement a stack using a linked list
of nodes
4 7 * 20 -
4
4 7 * 20 -
4
7
4
1. create an empty stack of integers
2. while there are more tokens
3. get the next token
4. if the first character of the token is a digit
5. push the token on the stack
6. else if the token is an operator
7. pop the right operand off the stack
8. pop the left operand off the stack
9. evaluate the operation
10. push the result onto the stack
11. pop the stack and return the result
Evaluating Postfix Expressions
(cont.)
4 * 7 4 7 * 20 -
7
4
1. create an empty stack of integers
2. while there are more tokens
3. get the next token
4. if the first character of the token is a digit
5. push the token on the stack
6. else if the token is an operator
7. pop the right operand off the stack
8. pop the left operand off the stack
9. evaluate the operation
10. push the result onto the stack
11. pop the stack and return the result
Evaluating Postfix Expressions
(cont.)
28 4 7 * 20 -
28
4 7 * 20 -
28
20
28
1. create an empty stack of integers
2. while there are more tokens
3. get the next token
4. if the first character of the token is a digit
5. push the token on the stack
6. else if the token is an operator
7. pop the right operand off the stack
8. pop the left operand off the stack
9. evaluate the operation
10. push the result onto the stack
11. pop the stack and return the result
Evaluating Postfix Expressions
(cont.)
28 - 20 4 7 * 20 -
20
28
1. create an empty stack of integers
2. while there are more tokens
3. get the next token
4. if the first character of the token is a digit
5. push the token on the stack
6. else if the token is an operator
7. pop the right operand off the stack
8. pop the left operand off the stack
9. evaluate the operation
10. push the result onto the stack
11. pop the stack and return the result
Evaluating Postfix Expressions
(cont.)
8 4 7 * 20 -
8
4 7 * 20 -
8
front = 0 size = 0
capacity = 5
front = 0 * size = 0
1
rear = 0 capacity = 5
front = 0 * size = 1
2
rear = 0 capacity = 5
rear = 1 +
front = 0 * size = 32
capacity = 5
rear = 1 +
rear = 2 /
public boolean offer(E item) {
if (size == capacity) {
reallocate();
}
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
q.offer('-');
front = 0 * size = 43
capacity = 5
+
rear = 2 /
public boolean offer(E item) {
rear = 3 - if (size == capacity) {
reallocate();
}
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
q.offer('A');
front = 0 * size = 54
capacity = 5
+
/
public boolean offer(E item) {
rear = 3 - if (size == capacity) {
reallocate();
rear = 4 A }
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
next = q.poll();
front = 0 * size = 5
4
capacity = 5
front = 1 +
/
public E poll() {
- if (size == 0) {
return null
rear = 4 A }
E result = theData[front];
front = (front + 1) % capacity;
size--;
return result;
result = '*' }
Implementing a Queue Using a
Circular Array (cont.)
next = q.poll();
* size = 4
3
capacity = 5
front = 1 +
front = 2 /
public E poll() {
- if (size == 0) {
return null
rear = 4 A }
E result = theData[front];
front = (front + 1) % capacity;
size--;
return result;
result = '+' }
Implementing a Queue Using a
Circular Array (cont.)
q.offer('B');
rear = 0 *B size = 3
4
capacity = 5
+
front = 2 /
public boolean offer(E item) {
- if (size == capacity) {
reallocate();
rear = 4 A }
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
q.offer('C');
rear = 0 B size = 4
5
capacity = 5
rear = 1 +
C
front = 2 /
public boolean offer(E item) {
- if (size == capacity) {
reallocate();
A }
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
q.offer('D');
B size = 5
capacity = 5
rear = 1 +
C
front = 2 /
public boolean offer(E item) {
- if (size == capacity) {
reallocate();
A }
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.) theData
q.offer('D'); B
rear = 1 +
C
B size = 5 front = 2 /
capacity = 5
+
C -
rear = 1
A
front = 2 /
private void reallocate() {
- int newCapacity = 2 * capacity;
E[] newData = (E[])new Object[newCapacity];
A int j = front;
for (int i = 0; i < size; i++) {
newData[i] = theData[j];
j = (j + 1) % capacity;
}
front = 0;
rear = size – 1;
capacity = newCapacity;
theData = newData;
newCapacity = 10 }
Implementing a Queue Using a Circular
Array (cont.)
newData
theData
i = 0 B
q.offer('D');
rear = 1 +
C
size = 5 j = 2
front = 2 /
capacity = 5
-
A
i = 0 / q.offer('D'); B
i = 1 rear = 1 +
C
size = 5 j = 2
front = 2 /
capacity = 5 j = 3
-
A
/ q.offer('D'); B
i = 1 - rear = 1 +
C
size = 5 front = 2 /
i = 2 capacity = 5 j = 3
-
A j = 4
/ q.offer('D'); B j = 0
- rear = 1 +
C
size = 5 front = 2 /
i = 2 A
capacity = 5
-
i = 3 A j = 4
/ q.offer('D'); B j = 0
j = 1
- rear = 1 +
C
size = 5 front = 2 /
A
capacity = 5
-
i = 3 B A
i = 4
private void reallocate() {
int newCapacity = 2 * capacity;
E[] newData = (E[])new Object[newCapacity];
int j = front;
for (int i = 0; i < size; i++) {
newData[i] = theData[j];
j = (j + 1) % capacity;
}
front = 0;
rear = size – 1;
capacity = newCapacity;
theData = newData;
newCapacity = 10 }
Implementing a Queue Using a Circular
Array (cont.)
newData
theData
/ q.offer('D'); B
j = 1
- rear = 1 +
C
size = 5 j = 2
front = 2 /
A
capacity = 5
-
B A
i = 4 C
private void reallocate() {
i = 5 int newCapacity = 2 * capacity;
E[] newData = (E[])new Object[newCapacity];
int j = front;
for (int i = 0; i < size; i++) {
newData[i] = theData[j];
j = (j + 1) % capacity;
}
front = 0;
rear = size – 1;
capacity = newCapacity;
theData = newData;
newCapacity = 10 }
Implementing a Queue Using a Circular
Array (cont.)
newData
theData
front = 0 / q.offer('D'); B
- rear = 1 +
C
size = 5 j = 2
front = 2 /
A 10
capacity = 5
-
B A
rear = 4 C
private void reallocate() {
i = 5 int newCapacity = 2 * capacity;
E[] newData = (E[])new Object[newCapacity];
int j = front;
for (int i = 0; i < size; i++) {
newData[i] = theData[j];
j = (j + 1) % capacity;
}
front = 0;
rear = size – 1;
capacity = newCapacity;
theData = newData;
newCapacity = 10 }
Implementing a Queue Using a Circular
Array (cont.)
theData
front = 0 / q.offer('D');
-
size = 5
6
A 10
capacity = 5
B
rear = 4 C
public boolean offer(E item) {
rear = 5 D
if (size == capacity) {
reallocate();
}
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
Listing 4.12 (ArrayQueue, pages ?)
Implementing Class
ArrayQueue<E>.Iter (cont.)
private class Iter implements
• Just as for class Iterator<E> {
ListQueue<E>, we must private int index;
implement the missing private int count = 0;
Queue methods and an inner
class Iter to fully implement public Iter() {
the Queue interface index = front;
}
@Override
public boolean hasNext() {
return count < size;
}
....
Implementing Class
ArrayQueue<E>.Iter (cont.)
private class Iter implements
• Just as for class Iterator<E> {
ListQueue<E>, we must private int index;
implement the missing private int count = 0;
Queue methods and an inner
class Iter to fully implement public Iter() {
the Queue interface index = front;
}
....
Implementing Class
ArrayQueue<E>.Iter (cont.)
private class Iter implements
• Just as for class Iterator<E> {
ListQueue<E>, we must private int index;
implement the missing private int count = 0;
Queue methods and an inner
class Iter to fully implement public Iter() {
the Queue interface index = front;
}
....
Implementing Class
ArrayQueue<E>.Iter (cont.)
private class Iter implements
• Just as for class Iterator<E> {
ListQueue<E>, we must private int index;
implement the missing private int count = 0;
Queue methods and an inner
class Iter to fully implement public Iter() {
the Queue interface index = front;
}
....
Implementing Class
ArrayQueue<E>.Iter (cont.)
private class Iter implements
• Just as for class Iterator<E> {
ListQueue<E>, we must private int index;
implement the missing private int count = 0;
Queue methods and an inner
class Iter to fully implement public Iter() {
the Queue interface index = front;
}
....
Implementing Class
ArrayQueue<E>.Iter (cont.)
@Override
• Just as for class
public E next() {
ListQueue<E>, we must
if (!hasNext()) {
implement the missing throw new
Queue methods and an inner NoSuchElementException();
class Iter to fully implement }
the Queue interface E returnValue = theData[index];
index = (index + 1) % capacity;
count+;
next() returns the
element at position return returnValue;
}
index and increments
Iter's fields index and
@Override
count
public void remove {
throw new
UnsupportedOperationException();
}
}
Implementing Class
ArrayQueue<E>.Iter (cont.)
@Override
• Just as for class
public E next() {
ListQueue<E>, we must
if (!hasNext()) {
implement the missing throw new
Queue methods and an inner NoSuchElementException();
class Iter to fully implement }
the Queue interface E returnValue = theData[index];
index = (index + 1) % capacity;
count+;
remove() throws an return returnValue;
exception because }
removing an item other
than the first item violates @Override
the queue's contract public void remove {
throw new
UnsupportedOperationException();
}
}
Comparing the Three Implementations
Computation time
All three implementations are comparable in terms of
computation time
All operations are O(1) regardless of implementation
Although reallocating an array is O(n), its is amortized
over n items, so the cost per item is O(1)
Comparing the Three Implementations
(cont.)
Storage
Linked-list implementations require more storage due to the
extra space required for the links
Each node for a single-linked list stores two references (one for the
data, one for the link)
Each node for a double-linked list stores three references (one for
the data, two for the links)
A double-linked list requires 1.5 times the storage of a single-
linked list
A circular array that is filled to capacity requires half the storage
of a single-linked list to store the same number of elements,
but a recently reallocated circular array is half empty, and
requires the same storage as a single-linked list
The Deque Interface
Section 4.8
Deque Interface
A deque (pronounced "deck") is short for double-ended queue
A double-ended queue allows insertions and removals from
both ends
The Java Collections Framework provides two
implementations of the Deque interface
ArrayDeque
LinkedList