Algorithms - Week 2-0 Stacks and Queues

modular programming

  • client, program using operations defined in interface
  • implementation, actual code implementing operations
  • interface, description of data type, basic operations

Stack

linked-list representation, maintain pointer to first node in a linked list; insert/remove from front.

array implementation

Defect. Stack overflows when N exceeds capacity. (stay tuned)

Stack considerations

Overflow and underflow.

  • Underflow: throw exception if pop from an empty stack.
  • Overflow: use resizing array from array implementation. (stay tuned)

Null items. We allow null items to be inserted.

Loitering. Holding a reference to an object when it is no longer needed.

public String pop() {
    return s[--N];
}

public String pop() {
    String item = s[--N];
    s[N] = null;
    return item;
}

this version avoids “loitering”: garbage collector can reclaim memory only if no outstanding references

Resizing Arrays

If array is full, create a new array of twice the size, and copy items.

Efficient solution.

  • push(): double size of array s[] when array is full.
  • pop(): halve size of array s[] when array is one-quarter full.

Amortized analysis. Average running time per operation over a worst-case sequence of operations.

Linked-list implementation.

  • Every operation takes constant time in the worst case.
  • Uses extra time and space to deal with the links.

Resizing-array implementation.

  • Every operation takes constant amortized time.
  • Less wasted space.

That’s a trade off that the client can make if I want that guaranteed, if I want to be sure that every operation is going to be fast use a linked-list and if I don’t need that guarantee, if I just care about the total amount of time I’ll probably use the resizing array because the total will be much less because individual operations are fast.

So, even with these simple data structures, we have really important trade offs that actually make a difference in lots of practical situations.

Queues

linked-list representation

  • Maintain pointer to first and last nodes in a linked list.

resizing array implementation

  • Update head and tail modulo the capacity.

Generics

Attempt 1. Implement a separate stack class for each type.

  • Rewriting code is tedious and error-prone.
  • Maintaining cur-and-pasted code is tedious and error-prone.

Attempt 2. Implement a stack with items of type Object.

  • Casting is required in cliend.
  • Casting is error-prone: run-time if types mismatch.

Attempt 3. Java generics.

  • Avoid casting in client.
  • Discover type mismatch errors at compile-time instead of run-time.

    Stack<Apple> s = new Stack<Apple>();
    
    public class Stack<Item> {
        public FixedCapacityStack(int capacity) {
            // s = new Item[capacity]; // not allowed in Java
            s = (Item[]) new Object[capacity];
        }
    }
    

Wrapper type.

  • Each primitive type has a wrapper object type.
  • Ex: Integer is wrapper type for int.

Autoboxing. Automatic cast between a primitive type and its wrapper.

Iterators

Design challenge. Support iteration over stack items by client, without revealing the internal representation of the stack.

Java solution. Make stack implement the Iterable interface.

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

for (String s : stack) {
    StdOut.println(s);
}

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

import java.util.Iterator;
// linked-list implementation
public class Stack<Item> implements Iterable<Item> {
    public Iterator<Item> iterator() {
        return new ListIterator();
    }
    private class ListIterator implements Iterator<Item>
    {
        private Node current = first;
        public boolean hasNext() {
            return current != null;
        }
        public Item next() {
            Item item = current.item;
            current = current.next;
            return item;
        }
    }
}

// array implementation
public class Stack<Item> implements Iterable<Item> {
    public Iterator<Item> iterator() {
        return new ReverseArrayIterator();
    }

    private class ReverseArrayIterator implements Iterator<Item> {
        private int i = N;
        public boolean hasNext() {
            return i > 0;
        }
        public void remove() {
            /* not supported */
        }
        public Item next() {
            return s[--i];
        }
    }
}

Bag API

Adding items to a collection and iterating (when order doesn’t matter).

Implementation. Stack (without pop) or queue (without dequeue).

Stack and Queue Applications

Java collections library

List interface. java.util.List is API for sequence of items.

Implementations.

java.util.ArrayList uses resizing array;

java.util.LinkedList uses linked list.

java.util.Stack

  • Supports push(), pop(), and iteration.
  • Also implements java.util.List interface
  • Bloated and poorly-designed API

java.util.Queue. An interface, not an implementation of a queue.

Best practices. Use our implementations of Stack, Queue, and Bag.

Stack applications

Dijkstra’s two-stack algorithm

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

  • Value: push onto the value stack.
  • Operator: push onto the operator stack.
  • Left parenthesis: ignore.
  • Right parenthesis: pop operator and two value; push the result of applying that operator to those values onto the operand stack.

你可能感兴趣的:(Algorithms - Week 2-0 Stacks and Queues)