Why Stack?

Excellenct usage example of stack: Two-stack algorithm. [E. W. Dijkstra]

Goal

Evaluate infix expressions.

( 1 + ( ( 2 + 3 ) * ( 4 * 5) ) )

Algorithm

Go

stack-ex-two-stack-algroithms

Implement

public class Evaluate{
    public static void main(String[] args){
        Stack<String> ops = new Stack<String>();
        Stack<Double> vals = new Stack<Double>();
        while (!StdIn.isEmpty()){
            String s = StdIn.readString();
            if ( s.equals("(") ){} // ignore
            else if ( s.equals("+") || s.equals("*") ){ // push operator
                ops.push(s);
            }
            else if ( s.equals(")") ){
                String op = ops.pop();
                if ( op.equals("+") ){
                    vals.push(vals.pop() + vals.pop());
                }
                else if ( op.equals("*") ){
                    vals.push(vals.pop() * vals.pop());
                }
            }
            else{ // number
                vals.push(Double.parseDouble(s));
            }
        }
        StdOut.println(vals.pop());
    }
}

Further observations

( 1 ( ( 23 + ) ( 4 5 *) * ) + )
1 2 3 + 4 5 * * +

观察到所有运算符都伴随着),那么直接以运算符为出栈计算条件。

Style

Stack

Interface

public class StackOfStrings{
    StackOfStrings();
    void push(String item);
    String pop();
    boolean isEmpty();
}

Implement by Linked list

public class StackOfStrings{
    private class node{
        String item;
        Node next;
    }
    private first = null;
    
    //public StackOfStrings()

    public void push (String item){
        Node oldFirst = first;
        first = new Node();
        first.item = item;
        first.next = oldFirst;
    }

    public String pop(){
        String ret = first.item;
        first = first.next;
        return ret;
    }
    
    public boolean isEmpty(){
        return first == null;
    }
}

Implement by Array

public class StackOfStrings{
    private String[] s;
    private int N = 0; // ahead of exist data
    
    // cap: capacity
    public StackOfStrings(int cap){
        s = new String[cap];}
    
    public boolean isEmpty(){
        return N == 0;}

    public void push(String item){
        s[N++] = item;}

    public String pop (){
        String ret = s[--N];
        // for java avoid 'loitering'
        S[N] = null; // remove the reference of the poped one
        return ret;}
}

Defect: determine the maximum capacity ahead of time

Implement by Resizing Array

Grow size

To realize a resizing array stack, we have 2 approaches:

  1. Create a new array 1 size bigger mirrors old array and +1 item once push
  2. Create a new array TWICE bigger mirror old array once FULL(term: Amortize Analyse)

But App[1] is to expansive:

App[2] is cheaper:

Shrink size

A better solution is:

Here’s an example:

resize-array-shirk-strategy

Comparison

Memeory usage

Implementation

Queues

Interface

public class QueueOfStrings{
    QueueOfStrings();
    void enqueue(String item);
    String dequeue();
    boolean isEmpty();
}

Implement by Linked list

public class QueueOfStrings{
    private class Node{
        String item;
        Node next;
    }
    private Node first, last;

    public void enqueue(String item){
        Node oldlast = last;
        last = new Node();
        last.item = item;
        last.next = null;
        if (isEmpty()){
            first = last;
        }
        else{
            oldlast.next = last;    
        }
    }
    public String dequeue(){
        String ret = first.item;
        first = first.next;
        if(isEmpty()){
            last = null;
        }
        return ret;
    }
    public boolean isEmpty(){
        return first == null;
    }
}

Generics

Usage

Stack<Apple> s = new Stack<Apple>();
Apple a = new Apple();
s.push(a); // OK
Orange b = new Orange();
s.push(b); // Error
a = s.pop();

Implement

public class FixedCapacityStack<Item>{
    private Item[] s;
    // N is 1 ahead of the current item
    private int N = 0;
    public FixedCapacityStack(int cap){
        // Ugly cast, but 
        // s = new Item[cap]; 
        // is not allowed in the Java
        s = (Item[]) new Object[cap];
    }
    public boolean isEmpty(){
        return N == 0;
    }
    public void push(Item item){
        s[N++] = item;
    }
    public Item pop(){
        return s[--N];
    }
}

Autoboxing

for the primative types, like int, double:

Stack<Integer> s = new Stack<Integer>();
s.push(17); // = s.push(Integer.valueOf(17));
int a = s.pop(); // = s.pop().intValue();

Iteration

Keep the internal representation black by implement the java.lang.Iterable interface.

public interface Iterable<Item>{
    Iterator<Item> iterator();
}
public interface Iterator<Item>{
    boolean hasNext();
    Item next();
    void remove(); // optional
}

So, the data structure can be used like:

for (String s : stack){
    StdOut.println(s);
}
// or
Iterator<String> i = stack.iterator();
while (i.hasNext()){
    String s = i.next();
    StdOut.println(s);
}

Implementation

Stack iterator: linked-list implementation

import java.until.Iterator;
public class Stack<Item> implements Iterable<Item>{
    ...
    public Iterator<Item> iterator(){
        return new ListIterator();
    }
    private class ListIterator implements Iterator<Item>{
        // current is the next, which has not been poped
        private Node current = first;
        public boolean hasNext(){
            return current != null;
        }
        public void remove(){ /* not supported */ }
        public Item next(){
            Item ret = current.item;
            current = current.next;
            return ret;
        }// end ListIterator.next()
    }// end internal class: ListIterator
}// end public class: Stack