CSE143 Notes for Wednesday, 10/12/11

We continued our discussion of stacks and queues. I mentioned that we are using the standard Java stack and queue classes, but I am limiting what operations you can use with them. For Stack<E>, we are using the Stack<E> implementation and we are using the following operations:

    public void push(E value);  // push given value onto top of the stack
    public E pop();             // removes and returns the top of the stack
    public boolean isEmpty();   // returns whether or not stack is empty
    public int size();          // returns number of elements in the stack
For Queue<E>, we are using the LinkedList<E> implementation and we are using the following operations:

    public void add(E value);  // inserts given value at the end of the queue
    public E remove();         // removes and returns the front of the queue
    public boolean isEmpty();  // returns whether or not queue is empty
    public int size();         // returns number of elements in the queue
We know that for arrays, it is possible to construct arrays that store different types of data:

But arrays are a special case in Java. If they didn't already exist, we couldn't easily add them to Java. Instead, Java now allows you to declare generic classes and generic interfaces. For example, the Queue interface and the LinkedList class are similar to an array. Instead of declaring ordinary Queue objects, we declare Queue<E> where E is some type (think of E as being short for "Element type"). The "E" is a type parameter that can be filled in with the name of any class.

For example, suppose, we want a Queue of Strings. We describe the type as:

        Queue<String>
When we construct an ordinary object like an ArrayIntList, we say:
         ArrayIntList list = new ArrayIntList();
Imagine replacing the occurrences of "ArrayIntList" with "Queue<String>" for the variable and "LinkedList<String>" for the call on new and you'll see how to construct a Queue<String>:
        Queue<String> list = new LinkedList<String>();
Then I then spent a little time discussing the issue of primitive data versus objects. Even though we can construct a Queue<E> for any class E, we can't construct a Queue<int> because int is a primitive type, not a class. To get around this problem, Java has a set of classes that are known as "wrapper" classes that "wrap up" primitive values like ints to make them an object. It's very much like taking a candy and putting a wrapper around it. For the case of ints, there is a class known as Integer that can be used to store an individual int. Each Integer object has a single data field: the int that it wrapped up inside.

Java also has quite a bit of support that makes a lot of this invisible to programmers. If you want to put int values into a Queue, you have to remember to use the type Queue<Integer> rather than Queue<int>, but otherwise Java does a lot of things for you. For example, you can construct such a list and add simple int values to it:

        Queue<Integer> numbers = new LinkedList<Integer>();
        numbers.add(18);
        numbers.add(34);
In the two calls on add, we are passing simple ints as arguments to something that really requires an Integer. This is okay because Java will automatically "box" the ints for us (i.e., wrap them up in Integer objects). We can also refer to elements of this list and treat them as simple ints, as in:

        int product = numbers.remove() * numbers.remove();
The calls on numbers.remove return references to Integer objects and normally you wouldn't be allowed to multiply two objects together. In this case Java automatically "unboxes" the values for you, unwrapping the Integer objects and giving you the ints that are contained inside.

Every primitive type has a corresponding wrapper class: Integer for int, Double for double, Character for char, Boolean for boolean, and so on.

Then we spent time writing some client code to manipulate a queue. I said that we would use a queue of int values. So we wrote this line of code to construct one:

        Queue<Integer> q = new LinkedList<Integer>();
Notice that we use the interface for defining the variable. We only use the name of the implementation when we are calling new. All variables, parameters and return types should be defined using the interface, as we discussed on Monday.

Because the implementation includes a toString method, we can print the queue:

        System.out.println("q = " + q);
I asked people to help me write some code that would add 10 random values between 0 and 99 to the queue. We used a Random object which required us to include an import from java.util:

        Queue<Integer> q = new LinkedList<Integer>();
        Random r = new Random();
        for (int i = 0; i < 10; i++)
            q.add(r.nextInt(100));
        System.out.println("q = " + q);
which produced output like the following:

        q = [25, 19, 83, 0, 70, 52, 76, 33, 81, 54]
Then we explored how to put this code for generating a queue with random values into a method. I suggested that we have a size parameter indicating how many values to add to the queue. We found that the return type for the method should be Queue<Integer>:

        public static Queue<Integer> makeRandomQueue(int size) {
            Queue<Integer> q = new LinkedList<Integer>();
            Random r = new Random();
            for (int i = 0; i < size; i++)
                q.add(r.nextInt(100));
            return q;
        }
We then changed main to call this method and print the result:

        Queue<Integer> q = makeRandomQueue(10);
        System.out.println("q = " + q);
I then asked people how we could clear the queue so that it didn't have anything in it. We don't have an easy way to do it other than to call remove repeatedly until the queue is empty. That might end up being very expensive if the queue has a lot of values in it. Another approach is to simply construct a new empty queue:

        q = new LinkedList<Integer>();
I then said that I wanted to work with a stack as well, so I included this line of code:

        Stack<Integer> s = new Stack<Integer>();
I said that I wanted to write a method that would transfer values from the queue to the stack. We need a loop that will remove values from the queue as long as there are more values left to remove. We can accomplish this with a while loop and the isEmpty and remove methods:

        public static void queueToStack(Queue<Integer> q, Stack<Integer> s) {
            while (!q.isEmpty()) {
                int n = q.remove();
                ...
	    }
        }
Notice that the parameters are of type Queue<Integer> and Stack<Integer>, using the Queue and Stack interfaces rather than the LinkedList and Stack implementations.

This code removed things from the queue, but to add them to the stack, we have to include a call on push inside the loop. At first I showed this incorrect version:

        public static void queueToStack(Queue<Integer> q, Stack<Integer> s) {
            while (!q.isEmpty()) {
                int n = q.remove();
                s.push(q.remove());
	    }
        }
The problem with this version is that it calls remove twice each time through the loop. It should be calling push with the value of n. Someone pointed out that we could eliminate the line that involves n altogether and have just the second line of code, which is true. But I changed it to work with n:

        public static void queueToStack(Queue<Integer> q, Stack<Integer> s) {
            while (!q.isEmpty()) {
                int n = q.remove();
	        s.push(n);
	    }
        }
I added some code to main to report what was in the stack and queue after calling queueToStack:

        Queue<Integer> q = makeRandomQueue(10);
        System.out.println("q= " + q);
        Stack<Integer> s = new Stack<Integer>();
	queueToStack(q, s);
        System.out.println("after queueToStack:");
	System.out.println("    queue = " + q);
	System.out.println("    stack = " + s);
It produced output like the following:

        q = [75, 76, 53, 82, 88, 77, 63, 28, 86, 7]
        after queueToStack:
            queue = []
            stack = [75, 76, 53, 82, 88, 77, 63, 28, 86, 7]
Then I asked people how we could write a method that would find the sum of the values in this queue. It is a cumulative sum task, which involves initializing a sum variable to 0 outside the loop and then adding each value to the sum as we progress through the loop. Our first attempt looked like this:

        public static int sum(Queue<Integer> q) {
            int sum = 0;
            while (!q.isEmpty()) {
                int n = q.remove();
                sum += n;
            }
            return sum;
        }
When we called the method from main and printed the queue afterwards, we found that the queue is empty. As a side effect of calculating the sum, we destroyed the contents of the queue. This is generally not acceptable behavior.

Unfortunately, queues don't give us any peeking operations. We have no choice but to take things out of the queue. But we can restore the queue to its original form. How? Someone suggested using a second queue. That would work, but there is an easier way. Why not use the queue itself? As we remove values to be processed, we re-add them at the end of the list. Of course, then the queue never becomes empty. So instead of a while loop looking for an empty queue, we wrote a for loop using the size of the queue:

        public static int sum(Queue<Integer> q) {
            int sum = 0;
            for (int i = 0; i < q.size(); i++) {
                int n = q.remove();
                sum += n;
                q.add(n);
            }
            return sum;
        }
By printing the queue before and after a call on sum, we were able to verify that our new version preserved the queue.

Then I created a variation of makeRandomQueue that I called makeRandomStack for making a stack of random values. This was a fairly straightforward modification where we simply switched queue operations with stack operations:

        public static Stack<Integer> makeRandomStack(int size) {
            Random r = new Random();
            Stack<Integer> s = new Stack<Integer>();
            for (int i = 0; i < size; i++)
                s.push(r.nextInt(100));
            return s;
        }
In this case I had to use a different name because otherwise the signatures would match (both with a single parameter of type int). In Java, the return type is not part of the signature.

In place of queueToStack, we wrote a new method stackToQueue:

        public static void stackToQueue(Stack<Integer> s, Queue<Integer> q) {
            while (!s.isEmpty()) {
                int n = s.pop();
	        q.add(n);
	    }
        }
I then said I wanted to consider a variation of the sum method for stacks:

        public static int sum(Stack<Integer> s) {
            ...
        }
This method can also be called sum because the two methods have different signatures. Remember that a signature of a method is its name plus its parameters. These are both called sum and they both have just a single parameter, but the parameter types are different, so this is okay. This is called overloading a method and it is a common technique in Java.

So how do we write the sum method for stacks? At first I did a similar modification where I simply substituted stack operations for queue operations:

        int sum = 0;
        for (int i = 0; i < s.size(); i++) {
            int n = s.pop();
            sum += n;
            s.push(n);
        }
        return sum;
Unfortunately, this code didn't work. We saw output like this when we tested it from main:

        stack = [42, 19, 78, 87, 14, 41, 57, 25, 96, 85]
        sum = 850
The sum of these numbers is not 850. We're getting that sum because the loop pops the value 85 off the stack 10 different times and then pushes it back onto the top of the stack. With a queue, values go in at one end and come out the other end. But with a stack, all the action is at one end of the structure (the top). So this approach isn't going to work.

In fact, you can't solve this in a simple way with just a stack. You'd need something extra like an auxiliary structure. I said to consider how we could solve it if we had a queue available as auxiliary storage. Then we can put things into the queue as we take them out of the stack and after we have computed the sum, we can transfer things from the queue back to the stack using our queueToStack method:

        int sum = 0;
        Queue<Integer> q = new LinkedList<Integer>();
        for (int i = 0; i < s.size(); i++) {
            int n = s.pop();
            sum += n;
            q.add(n);
        }
	queueToStack(q, s);
        return sum;
This also didn't work. Here is a sample execution:

        initial stack = [32, 15, 54, 91, 47, 45, 88, 89, 13, 0]
        sum = 235
        after sum stack = [32, 15, 54, 91, 47, 0, 13, 89, 88, 45]
There are two problems here. Only half of the values were removed from the stack and those values now appear in reverse order. Why only half? We are using a for loop that compares a variable i against the size of the stack. The variable i is going up by one while the size is going down by one every time. The result is that halfway through the process, i is large enough relative to size to stop the loop. This is a case where we want a while loop instead of a for loop:

        int sum = 0;
        Queue<Integer> q = new LinkedList<Integer>();
        while (!s.isEmpty()) {
            int n = s.pop();
            sum += n;
            q.add(n);
        }
	queueToStack(q, s);
        return sum;
Even this is not correct. It finds the right sum, but it ends up reversing the values in the stack. Some people said, "Then why don't you use a stack for auxiliary storage?" That would solve the problem, but one of the things we are testing is whether you can figure out how to solve a problem like this given a certain set of tools. In this case, you are given auxiliary storage in the form of a queue.

The problem is that by transferring the data from the stack into the queue and then back into the stack, we have reversed the order. The fix is to do it again so that it goes back to the original. So we add two extra calls at the end of the method that move values from the stack back into the queue and then from the queue back into the stack:

        public static int sum(Stack<Integer> s) {
            int sum = 0;
            Queue<Integer> q = new LinkedList<Integer>();
            while (!s.isEmpty()) {
                int n = s.pop();
                sum += n;
                q.add(n);
            }
	    queueToStack(q, s);
	    stackToQueue(s, q);
	    queueToStack(q, s);
            return sum;
        }
This is the correct version of the method which appears in handout #8.


Stuart Reges
Last modified: Thu Oct 20 10:55:58 PDT 2011