CIS 121 April 5, 2000


There are three announcements today.
  1. Recall that Advising has begun. Freshmen and Sophomores must sign up at http://www.cis.usouthal.edu/advising. Juniors, Seniors, and Graduate Students must sign up on the board in FCW 20. Time is running short. Advising for Juniors, Seniors, and Graduate students will only last for two weeks. After that time, even if you meet with an advisor, there will still be a block placed on your registration. So make sure you sign up soon. Advising for Freshmen and Sophomores will last for three weeks. Each Freshmen and Sophomore is assigned a particular week to be advised. You will be told what week you have been assigned when you sign up.
  2. There are three scholarships and one fellowship available to deserving students. You can find applications in the main School of CIS office, FCW 20, or you can apply online at http://www.cis.usouthal.edu/scholarships/2000ScholarshipFellowship.html. The deadline has been extended until next Monday, April 10, 2000.
  3. The Annual ACM Banquet will be held this year on the Battleship Alabama on April 22. The winners of the scholarships and fellowships will be announced at the banquet. The cost of the banquet until this Friday is $22.50. After Friday the cost will be $25. The banquet will consist of a nice dinner and dancing after dinner on the Battleship. All students are encouraged to attend. If you need more information, you may contact the ACM Faculty sponsor, Dr. David Langan.

Stacks

We will continue our discussion of stacks. Recall that we look at several methods naturally associated with stacks.

push - place an element on to the stack.

pop - take an element off the top of the stack.

peek - examine the element on the top of the stack without taking it.

empty - determine whether or not the stack is empty.

search - search for the position of a particular element of the stack.

There is a predefined class called Stack in the java.util package that implements these methods for us. The interface of that class is

public class Stack extends Vector {
   public Stack();

   public boolean empty();
   public Object peek();
   public Object pop();
   public Object push(Object item);
   public int search(Object o);
}
The example that we have looked at to illustrate the use of stacks is converting arithmetic expressions from infix notation to postfix or Reverse Polish Notation.

An arithmetic expression is classified as infix if the arithmetic operator is placed between the operands on which it is to operate. Notice that this requires the use of parentheses in some cases.

In writing compilers it is more convenient to evaluate an arithmetic expression if it is written in Reverse Polish Notation.

We have looked at the simple example of converting the infix expression a+b into the Reverse Polish Notation ab+. Let us look again at the algorithm used.

Recall that we surround the expression with brackets. We place the following priorities on valid arithmetic operators.

Operator Priority
[ 0
{ 1
- 2
+ 2
/ 3
* 3
^ 4

To convert infix notation to Reverse Polish Notation we use the following algorithm found on page 529 of your book.

for every character in the infix expression
      if next character is closing parenthesis ')'
         pop operator from stack
         while operator not opening parenthesis '('
            store operator in string buffer
            pop operator from stack
      else if next character is closing bracket ']'
         pop operator from stack
         while operator not opening bracket '['
            store operator in string buffer
            pop operator from stack
      else if next character is opening parenthesis '(' or opening bracket '['
         push next character on to stack
      else if next character is arithmetic operator
         while priority of next character is <= priority of stack top operator
            pop operator from stack
            store operator in string buffer
         push next character on to stack
      else
         store next character in string buffer
Let us look at this algorithm in action.

Reverse Polish Notation

Now let us look at how this algorithm may be implemented.

import java.util.*;

public class ReversePolishNotation {
   String inputString;
   String outputString = "";

   public ReversePolishNotation(String s) {
      inputString = s;
   }

   private int priority(String s) {
      int operatorPriority = -1;

      if (s.equals("["))
         operatorPriority = 0;
      else if (s.equals("("))
         operatorPriority = 1;
      else if (s.equals("+"))
         operatorPriority = 2;
      else if (s.equals("-"))
         operatorPriority = 2;
      else if (s.equals("*"))
         operatorPriority = 3;
      else if (s.equals("/"))
         operatorPriority = 3;
      else if (s.equals("^"))
         operatorPriority = 4;

      return(operatorPriority);
   }

   private void convert() {
      Stack myStack = new Stack();
      String nextCharacter;
      String topOfStack;

      for (int i = 0;i<=inputString.length()-1;i++) {
         nextCharacter = inputString.substring(i,i+1);
         System.out.println(nextCharacter);
         if (nextCharacter.equals(")")) {
            topOfStack = (String)myStack.pop();
            while (!topOfStack.equals("(")) {
               outputString += topOfStack;
               topOfStack = (String)myStack.pop();
            }
         } else if (nextCharacter.equals("]")) {
            topOfStack = (String)myStack.pop();
            while (!topOfStack.equals("[")) {
               outputString += topOfStack;
               topOfStack = (String)myStack.pop();
            }
         } else if (nextCharacter.equals("(") ||
                    nextCharacter.equals("[")) {
            myStack.push(nextCharacter);
         } else if (nextCharacter.equals("+") ||
                    nextCharacter.equals("-") ||
                    nextCharacter.equals("*") ||
                    nextCharacter.equals("/") ||
                    nextCharacter.equals("^")) {
            topOfStack = (String)myStack.peek();
            while (priority(nextCharacter) <= priority(topOfStack)) {
               topOfStack = (String)myStack.pop();
               outputString += topOfStack;
               topOfStack = (String)myStack.peek();
            }
            myStack.push(nextCharacter);
         } else {
            outputString += nextCharacter;
         }             
      }
   }

   public String toString() {
      return(outputString);
   }

   public static void main(String[] args) {
      ReversePolishNotation myConverter = new ReversePolishNotation("[a+b]");
      myConverter.convert();
      System.out.println(myConverter);
   }
}
You may download this source code.

Source Code.

Notice how abstraction has come into play. We have worked with a Stack and seen its methods work without any consideration of how those methods are implemented. But now let's look at some implementation issues.

Recall that the Stack class extends the Vector class.

As we have seen before a simple example of a data structure is an array. An array can hold primitive data types and object references. A limitation of an array is that we have to specify how large it is. We can access the size of an array by the length variable associated with arrays. However, we cannot change the size of the array. When new elements are added to an array we must write the code to move other elements if we need to.

The Vector class provides us with an easier way to approach this problem. Unlike an array, a Vector can only hold object references. Therefore if we wish to place a primitive data type into a Vector we have to place it in a wrapper class. Another advantage of a Vector is that a Vector can grow dynamically. We can keep adding elements to a Vector and it will expand to hold those object references. We can control both the initial capacity and the growth factor of the Vector. There are three constructors for the Vector class.

Vector() - creates an empty vector with initial capacity 10.

Vector(int cap) - creates an empty vector with initial capacity cap.

Vector(int cap,int grow) - creates an empty vector with initial capacity
                           cap and growth factor grow.
There are also several methods available in the Vector class. Some of the methods are: Notice one important thing about a Vector. Unlike arrays, every element in a Vector is an Object. Recall that the class structure in Java is hierarchical and every class inherits from the Object class. So every object in Java is of type Object. So we must keep track of what type of Object is placed in the Vector because we will have to cast it back to its original type after we retrieve it from the Vector.

Since Vectors can give us most of the same functionality of an array and can grow dynamically, one might wonder why we don't just abandon arrays and use Vectors. One reason for that is that not every language supports Vectors, whereas almost every language supports arrays.

As a comparison, let us consider today how we would implement the methods of a stack using arrays instead of Vectors. One obvious limitation we have is that we will have to specify the maximum size of the Stack.

public class Stack {






}