Unit 6. Stacks and Their Applications: Outcomes Summary
Unit 6. Stacks and Their Applications: Outcomes Summary
Unit 6. Stacks and Their Applications: Outcomes Summary
94
USING STACKS
CS2310 DSAJ
2014/2015
You will:
92
Unit Objectives
93
Introduction
In Unit 3, we discussed the StackADT and, in Unit 4, looked at a simple array-based implementation of this ADT: namely ArrayStack. In Unit 5, we looked at a straight-forward implementation
of StackADT using a linked list. In this unit, we will examine the use of stack to solve certain types
of programming problems.
94
Using Stacks
Stacks are particularly helpful when solving certain types of problems in Computer Science.
One common application of a stack is to implement a multiple undo facility in an application.
In order to perform undo operations, the application needs to keep track of the sequence of
recent operations in reverse order. When an operation is performed, a record of it is pushed
onto a stack and when the undo function is invoked, the most recent operation can be popped
off the stack for undoing.
A classic use of stacks is in the run-time environment of a programming language such as Java.
As method calls occur, the run-time system pushes an activation record onto the run-time stack.
When each method completes its execution, its activation record is popped off the stack and using
information in this record control returns to the method that called it. The activation record
on the top of the stack corresponds to the executing method. Hence,the run-time system can
roll back the chain of method calls as each called method finishes its execution by popping
the activation record off the top of the stack.
Another suitable use of a stack is for keeping track of alternatives in maze traversal or in other
trial and error algorithms.
Stacks are also useful in an application that evaluates postfix expressions.
We will consider these last two examples in more detail below.
SHS Wong
1/14
6. 95
95
CS2310 DSAJ
2014/2015
We can traverse a maze using a trial-and-error approach. We need to use a stack to keep track of
moves that have not yet been tried in order to avoid repeatedly trying paths that have already
been followed.
A maze can be represented by a grid of 1s and 0s. A 0 represents a blockage (i.e. part of a
wall) and a 1 represents a path. Thus the grid can be represented as a two-dimensional array
of int values in Java. A successful traversal of the maze means that a continuous path of 1s
is found from the starting point (the top left corner of the grid in our example) to the finishing
point (the bottom right corner of the grid). See Figure 44.
SHS Wong
2/14
6. 95
CS2310 DSAJ
2014/2015
We push these positions onto the stack (see the methods traverse, valid and push new pos
in the class Maze). The order in which the possible valid moves are pushed onto the stack is
not critical; the algorithm works whatever the order.
When we want to make a move, we pop one position from the stack and move to this position.
(See the method traverse in the class Maze). As we move on, more valid moves are found
and pushed into the stack and one by one positions are marked as visited. At each move, we
pop one location from the stack. In this way, the maze traversal is carried out until the target
location is reached (or if there is no possible path from start to finish until the stack is empty
and no valid moves remain). There is no chance of getting into an infinite loop as already
visited cells are never revisited.
Note that this implementation of maze search enables the target location to be found, but the
path which leads from the start position to the finish is not recorded by the algorithm.
The Java solution consists of three tailor-written classes plus an implementation of the interface
StackADT. Figure 46 shows the class ArrayStack as the implementing class, but the class
LinkedStack could be used instead with only minor changes to the code. (Exercise!).
Figure 46: A UML class diagram for the maze traversal application
Class Maze
1
package maze;
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Maze.java
*
* Represents a maze of characters. The goal is to get from the
* top left corner to the bottom right, following a path of 1s.
*
* @author Lewis/Chase
* @author S H S Wong
* @author A Barnes
* @version 10-2009
*/
import dsaj.*;
SHS Wong
3/14
6. 95
CS2310 DSAJ
2014/2015
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1,
0,
0,
1,
1,
0,
1,
0,
1,
1
1
0
1
1
1
1
0
1
},
},
},
},
},
},
},
},
} };
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
* A method for pushing a new position onto the stack. A new position (x,
* y) is pushed onto the stack ONLY IF it is a valid position that is it
* is inside the grid, is not a wall and has not been visited already.
*/
private void push_new_pos(int x, int y) {
if (valid(x, y)) {
stack.push(new Position(x, y));
}
}
46
47
48
49
50
51
52
53
54
55
56
/*******************************************************************
* Attempts to iteratively traverse the maze. It inserts special
* characters indicating locations that have been tried and that
* eventually become part of the solution. This method uses a
* stack to keep track of the possible moves that could be made.
*******************************************************************/
public boolean traverse() {
boolean done = false; // set true when the finish position is reached
Position pos;
stack.push(start);
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
SHS Wong
while (!(done)) {
pos = stack.pop();
grid[pos.getX()][pos.getY()] = TRIED; // this cell has been tried
if (pos.getX() == finish.getX() && pos.getY() == finish.getY())
done = true; // the maze is solved
else {
/*
* Get the four positions that are adjacent to the current
* position, i.e. the positions on the left, on the right,
* above and below the current position. These correspond
* to the potential moves. Each of such position is analysed
* to see if it is a valid move. Only valid moves will be
* pushed onto the stack.
*/
push_new_pos(pos.getX(), pos.getY() - 1);
push_new_pos(pos.getX(), pos.getY() + 1);
4/14
6. 95
CS2310 DSAJ
2014/2015
push_new_pos(pos.getX() - 1, pos.getY());
push_new_pos(pos.getX() + 1, pos.getY());
74
75
}
}
return done;
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
return result;
93
94
95
96
97
98
99
100
101
102
103
104
105
result += "\n";
}
return result;
106
107
108
109
110
Class Position
1
package maze;
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Position.java
* Represents a single position in a rectangular grid
*
* @author Lewis/Chase
* @author A Barnes
* @author S H S Wong
* @version 24-10-2010
*/
public class Position {
private int x;
private int y;
15
16
17
18
19
/**
* Constructs a position and sets the x and y coordinates
X coordinate of a position
* @param x
* @param y Y coordinate of the position
SHS Wong
5/14
6. 95
20
*/
Position(int x, int y) {
this.x = x;
this.y = y;
}
21
22
23
24
CS2310 DSAJ
2014/2015
25
/**
* Returns the x-coordinate value of the current position.
the X coordinate of the current position
* @return
*/
public int getX() {
return x;
}
26
27
28
29
30
31
32
33
/**
* Returns the y-coordinate value of the current position.
the Y coordinate of the current position
* @return
/
*
public int getY() {
return y;
}
34
35
36
37
38
39
40
41
Class MazeSearch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package maze;
/**
* MazeSearch.java
* Top-level class for the maze-search application.
* This application simulates the process of finding a path of 1s in a
* maze of characters (represented by 1s and 0s).
*
* @author Lewis/Chase
* @author S H S Wong
* @version 24-10-2010
*/
public class MazeSearch {
/*******************************************************************
* Creates a new maze, prints its original form, attempts to solve it,
* and prints out its final form.
*******************************************************************/
public static void main(String[] args) {
// Create the maze.
Maze labyrinth = new Maze();
20
21
22
23
24
25
/*
* Print the maze. At the beginning, the maze contains 0s (i.e.
* blockage) & 1s (i.e. path) only.
*/
System.out.println(labyrinth);
26
27
28
29
30
31
32
33
34
SHS Wong
/*
* labyrinth.traverse() is for traversing the maze. When a path is
* found at the end of the maze traversal, the value true is returned.
* Otherwise, false is returned.
*/
if (labyrinth.traverse())
System.out.println("The maze was successfully traversed!");
else
6/14
6. 96
CS2310 DSAJ
2014/2015
35
36
/*
* Print the maze after the traversal has taken place. As during the
* traversal, each visited position is marked with 3, the maze should
* now contain 0s, 1s & 3s.
*/
System.out.println(labyrinth);
37
38
39
40
41
42
43
44
For simplicity we only consider binary operators (i.e. those taking two operands).
SHS Wong
7/14
6. 96
CS2310 DSAJ
2014/2015
The class Postfix is a Java application for a simple command-line-based postfix evaluator.
This application demonstrates how a stack data structure can be used to aid the evaluation of
postfix expressions.
To simplify the example, let us assume that the operands of the expressions are integer literals.
This means that the stack in this application will work with Integer objects only.
In order not to cloud the presentation with try ...
input is a syntactically correct postfix expression.
1
2
3
4
5
6
7
package postfix;
//********************************************************************
//Postfix.java Authors: Lewis/Chase (Modified by Wong)
//
//Demonstrates the use of a stack to evaluate postfix expressions.
//********************************************************************
import java.util.Scanner;
8
9
10
11
12
13
14
15
16
17
18
try
{ /* A Scanner breaks its input into tokens using
* a delimiter pattern. A whitespace character is the
* default delimit pattern for a Scanner object.
*/
Scanner in = new Scanner(System.in);
19
20
21
22
23
24
25
do
{
26
27
28
29
30
31
32
33
34
35
36
37
38
}
while (again.equalsIgnoreCase("y"));
39
40
}
catch (Exception IOException)
{
System.out.println("Input exception reported");
}
41
42
43
44
45
46
47
SHS Wong
8/14
6. 96
CS2310 DSAJ
2014/2015
This Java application uses a LinkedStack object to model the stack, i.e. a stack modelled as
a linear linked structure. As far as the application is concerned, any implementation of a stack
would suffice.
The class PostfixEvaluator uses a LinkedStack object to evaluate a string representation
of posfix expressions. A PostfixEvaluator object is used by the class Postfix, which
defines a simple text-based interface for obtaining a postfix expression entered by the user via
the standard input stream.
1
2
3
4
5
6
7
8
9
package postfix;
//********************************************************************
//PostfixEvaluator.java Authors: Lewis/Chase (Modified by Wong)
//
//Represents an integer evaluator of postfix expressions. Assumes
//the operands are constants.
//********************************************************************
import dsaj.*;
import java.util.StringTokenizer;
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*******************************************************************
Sets up this evaluator by creating a new stack.
*******************************************************************/
public PostfixEvaluator() {
stack = new LinkedStack<Integer>();
}
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*******************************************************************
Evaluates the specified postfix expression. If an operand is
encountered, it is pushed onto the stack. If an operator is
encountered, two operands are popped, the operation is
evaluated, and the result is pushed onto the stack.
*******************************************************************/
public int evaluate(String expr) {
int op1, op2, result = 0;
String token;
/* Use a StringTokenizer object for breaking the input string
* into tokens.
*/
StringTokenizer tokenizer = new StringTokenizer (expr);
39
40
41
42
43
44
45
46
50
SHS Wong
47
48
49
9/14
6. 96
CS2310 DSAJ
2014/2015
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
return result;
71
72
73
/*******************************************************************
Determines if the specified token is an operator.
*******************************************************************/
private boolean isOperator (String token) {
return ( token.equals("+") || token.equals("-") ||
token.equals("*") || token.equals("/") || token.equals("%") );
}
74
75
76
77
78
79
80
81
/*******************************************************************
Peforms integer evaluation on a single expression consisting of
the specified operator and operands.
*******************************************************************/
private int evalSingleOp (char operation, int op1, int op2) {
int result = 0;
82
83
84
85
86
87
88
switch (operation)
case ADD:
result = op1 +
break;
case SUBTRACT:
result = op1 break;
case MULTIPLY:
result = op1 *
break;
case DIVIDE:
result = op1 /
break;
case REMAINDER:
result = op1 %
}
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
{
op2;
op2;
op2;
op2;
op2;
105
return result;
106
107
108
SHS Wong
10/14
6. 97
97
CLASS JAVA.UTIL.STACK
CS2310 DSAJ
2014/2015
Class java.util.Stack
Class java.util.Stack is part of JDK since version 1.0. The Java Platform Standard Edition
7 API Specification states (http://docs.oracle.com/javase/7/docs/api/java/util/
Stack.html):
The Stack class represents a last-in-first-out (LIFO) stack of objects. It extends class Vector with
five operations that allow a vector to be treated as a stack. The usual push and pop operations are
provided, as well as a method to peek at the top item on the stack, a method to test for whether the
stack is empty, and a method to search the stack for an item and discover how far it is from the top.
When a stack is first created, it contains no items.
J2SDK 7 defines a Stack class27 with operations such as pop, push, peek and empty (i.e. to
test if the stack is empty).
java.util.Stack extends java.util.Vector28 and therefore has some characteristics
that are not appropriate for a pure stack. For example, it inherits methods like: add, addAll,
addElement, capacity, contains, elementAt, indexOf, insertElementAt, remove,
removeAll, subList, trimToSize, etc. which violate the characteristic of a stack, i.e. access
of elements can only be done from the top of the stack.
Furthermore, the method search in java.util.Stack searches through the stack and returns whether the specified element exists in the stack, and, if so, its whereabout. As stacks
have openning at one end only, users of a stack should not be able to inspect the elements in
the stack, unless the element is at the top.
Classes java.util.Stack and java.util.Vector have been around since the original
version of Java (i.e. JDK 1.0). While java.util.Stack is not part of the Java Collections
Framework (JCF), java.util.Vector has been retrofitted to implement the java.util.List
interface, making it a member of the JCF since version 1.2.
Figure 48: java.util.Stack extends java.util.Vector and implements six different interfaces.
One way to reuse java.util.Stack as a pure stack is by means of a wrapper class. An
application programmer may define a wrapper class named PureStack, say, which has a
single field contents of type java.util.Stack. Class PureStack can define methods
that belongs to a pure stack, e.g. pop, push, peek and isEmpty. Each of these methods
simply calls the respective method in java.util.Stack to do the job.
27
SHS Wong
11/14
6. 99
98
CLASS JAVA.UTIL.ARRAYDEQUE
CS2310 DSAJ
2014/2015
Interface java.util.Deque
99
Class java.util.ArrayDeque
SHS Wong
12/14
6. 101
CLASS JAVA.UTIL.LINKEDLIST
100
Class java.util.LinkedBlockingDeque
CS2310 DSAJ
2014/2015
101
Class java.util.LinkedList
SHS Wong
13/14
6. 101
CLASS JAVA.UTIL.LINKEDLIST
CS2310 DSAJ
2014/2015
Learning Outcomes
You should now be able to:
-apply stack data structures appropriately to solve software
problems
-explain why java.util.Stack in JCF is not a pure implementation
of the stack ADT
-appreciate how usage of a stack is largely independent of how the
stack ADT is implemented
If not, go through the notes again, read the textbook and/or
consult the module tutor regarding queries on any of the learning
materials.
SHS Wong
14/14