A stack is a sequence of elements, to which elements can be added and removed -- elements are removed in the reverse order in which they were added, i.e., the last one added is the first one removed (LIFO).
We usually use the word push for adding an element to a stack, and the word pop for removing an element from a stack.
However, people naturally think in terms of state, so a popular way to specify an ADT is with some kind of "abstract, or high-level, implementation", in which we
For instance, to specify a stack, we could say:
But a purist might complain that this specification is, implicitly, suggesting a particular implementation. To be even more abstract, one can specify an ADT simply in terms of the series of operations that are allowable.
For instance, consider the following sequences:
Other operations that you sometimes want to provide:
Java provides a Stack class, in java.util.
numleft = 0 for each char in the string if char = ( then numleft++ if char = ) then numleft-- if numleft < 0 then unbalanced endfor if numleft = 0 then balanced else unbalancednumleft is being manipulated kind of like a stack. When a ( is encountered, imagine it is being pushed onto the stack. When a ) is encountered, a ( is popped off the stack. We never want to try to pop an empty stack, and at the end the stack should be empty.
Let's see how to do this explicitly with a stack. Using a stack makes it easier to extend this algorithm to more complicated situations.
create an empty stack for each char in the string if char = ( then push ( onto the stack if char = ) then pop ( off the stack if the pop causes an error then unbalanced endfor if stack is empty then balanced else unbalanced
Using java.util.Stack class:
import java.util.*; class Balance { public static void main(String argv[]) { char[] parens; parens = getInput(); // method that gets input sequence of parens Stack S = new Stack(); // create a new empty stack try { // the pop might throw an exception for (int i = 0; i < parens.length; i++) { if ( parens[i] == '(' ) S.push('('); // convert char to an object else // parens[i] == ')' Object o = S.pop(); // don't care about return value -- } // only possibility is '(' if (S.empty()) System.out.println("balanced"); else System.out.println("unbalanced"); } // end try catch (EmptyStackException e) { System.out.println("unbalanced"); } } }
Now suppose we can have 3 different kinds of parentheses: ( and ), [ and ], { and }. We can easily modify the program: When we encounter a ), we should pop off a (. When we encounter a ], we should pop off a [. When we encounter a }, we should pop off a {.
import java.util.*; class Balance3 { public static void main(String argv[]) { char[] parens; parens = getInput(); // method that gets input sequence of parens Stack S = new Stack(); // create a new empty stack try { // the pop might throw an exception for (int i = 0; i < parens.length; i++) { if ( ( parens[i] == '(' ) || ( parens[i] == '[' ) || ( parens[i] == '{' ) ) S.push(new Character(parens[i])); // convert char to object else { // parens[i] is a right paren char rightp = (( Character) S.pop()).charValue(); // cast returned object to Character, then // convert to char switch (parens[i]) { case ')' : if ( rightp != '(' ) { System.out.println("unbalanced"); return; } break; case ']' : if ( rightp != '[' ) { System.out.println("unbalanced"); return; } break; case '}' : if ( rightp != '{' ) { System.out.println("unbalanced"); return; } break; } } } // end for // finished processing the input sequence if (S.empty()) System.out.println("balanced"); else System.out.println("unbalanced"); } // end try catch (EmptyStackException e) { System.out.println("unbalanced"); } } }(*If you printed this out before 3/8/98, you got a version containing the postfix section with bugs. See the notes for 3/9/98 for corrected version.*)