《Thinking In Algorithm》02.Stacks,Queues,Linked Lists

来源:互联网 发布:mac远程控制 编辑:程序博客网 时间:2024/06/05 22:43

1.Stacks

The basic implementation of a stack is also called a LIFO (Last In First Out) to demonstrate the way it accesses data, since as we will see there are various variations of stack implementations.

There are basically three operations that can be performed on stacks . They are 

1) inserting an item into a stack (push). 

2) deleting an item from the stack (pop).

3) displaying the contents of the stack(pip).


1.Array implementation

An instance variable a[] with an array of strings to hold the stack items is clearly needed, but how big should it be? For the moment,We will finesse this problem by having the client provide an argument for the constructor that gives the maximum stack size. We keep the items in reverse order of their arrival. This policy allows us to add and remove items at the end without moving any of the other items in the stack.

此处利用的是ArrayList特性来解决数组未知大小的问题,ArrayList有别于Array,详细见 Array与ArrayList的区别

implement in java

import java.util.Iterator;public class ArrayStackOfStrings implements Iterable<String> {    private String[] a;  // holds the items    private int N;       // number of items in stack    public ArrayStackOfStrings(int max) {  a = new String[max];       }    public boolean isEmpty()            {  return (N == 0);           }    public void push(String item)       {  a[N++] = item;             }    public String pop()                 {  return a[--N];             }    public Iterator<String> iterator()  { return new ArrayIterator(); }    public class ArrayIterator implements Iterator<String> {        private int i = N-1;        public boolean hasNext() { return i >= 0; }        public String next()     { return a[i--]; }        public void remove()      { throw new UnsupportedOperationException(); }    }    public static void main(String[] args) {        int max = Integer.parseInt(args[0]);        ArrayStackOfStrings stack = new ArrayStackOfStrings(max);        while (!StdIn.isEmpty()) {            String item = StdIn.readString();            if (!item.equals("-")) stack.push(item);             else if (stack.isEmpty())  StdOut.println("BAD INPUT");             else                       StdOut.print(stack.pop() + " ");        }        StdOut.println();        // print what's left on the stack        StdOut.print("Left on stack: ");        for (String s : stack) {            StdOut.print(s + " ");        }        StdOut.println();    } } 

2.Implementing stacks with linked lists

  • Linked lists

a linked list is a data structure consisting of a group of nodes which together represent a sequence. Under the simplest form, each node is composed of a datum and a reference (in other words, a link) to the next node in the sequence; more complex variants add additional links. This structure allows for efficient insertion or removal of elements from any position in the sequence.

With object-oriented programming, implementing linked lists is not difficult. We start with a simple example of a class for the node abstraction:

class Node {    String item;    Node next; } 


  • Insert. Suppose that you want to insert a new node into a linked list. The easiest place to do so is at the beginning of the list. For example, to insert the string "not" at the beginning of a given linked list whose first node is first, we save first in oldfirst, assign to first a newNode, assign its item field to "not" and its next field to oldfirst.


  • Remove. Suppose that you want to remove the first node from a list. This operation is even easier: simply assign to first the valuefirst.next. Normally, you would retrieve the value of the item (by assigning it to some String variable) before doing this assignment, because once you change the value of first, you may not have any access to the node to which it was referring. Typically, the node object becomes an orphan, and the memory it occupies is eventually reclaimed by the Java memory management system.


These two operations take constant time (independent of the length of the list).

  • Implementing stacks with linked lists
Program LinkedStackOfStrings.java uses a linked list to implement a stack of strings. The implementation is based on a nested class Node like the one we have been using. Java allows us to define and use other classes within class implementations in this natural way. The class is private because clients do not need to know any of the details of the linked lists.

public class LinkedStackOfStrings {    private int N;          // size of the stack    private Node first;     // top of stack    // helper Node class    private class Node {        private String item;        private Node next;    }    // is the stack empty?    public boolean isEmpty() { return first == null; }    // number of elements on the stack    public int size() { return N; }    // add an element to the stack    public void push(String item) {        Node oldfirst = first;        first = new Node();        first.item = item;        first.next = oldfirst;        N++;    }    // delete and return the most recently added element    public String pop() {        if (isEmpty()) throw new RuntimeException("Stack underflow");        String item = first.item;      // save item to return        first = first.next;            // delete first node        N--;        return item;                   // return the saved item    }    // test client    public static void main(String[] args) {        int max = Integer.parseInt(args[0]);        LinkedStackOfStrings s = new LinkedStackOfStrings();        while (!StdIn.isEmpty()) {            String item = StdIn.readString();            if (!item.equals("-")) s.push(item);             else if (s.isEmpty())  StdOut.println("BAD INPUT");             else                   StdOut.print(s.pop());        }     } }

2.queue

queue supports the insert and remove operations using a FIFO discipline. By convention, we name the queue insert operation enqueueand the remove operation dequeue. Lincoln tunnel. Student has tasks that must be completed. Put on a queue. Do the tasks in the same order that they arrive.

public class Queue<Item> {   public boolean isEmpty();   public void enqueue(Item item);   public Item dequeue();}

  • Linked list implementation. Program Queue.java implements a FIFO queue of strings using a linked list. Like Stack, we maintain a referencefirst to the least-recently added Node on the queue. For efficiency, we also maintain a reference last to the least-recently added Node on the queue.


    import java.util.Iterator;import java.util.NoSuchElementException;public class Queue<Item> implements Iterable<Item> {    private int N;         // number of elements on queue    private Node first;    // beginning of queue    private Node last;     // end of queue    // helper linked list class    private class Node {        private Item item;        private Node next;    }   /**     * Create an empty queue.     */    public Queue() {        first = null;        last  = null;    }   /**     * Is the queue empty?     */    public boolean isEmpty() {        return first == null;    }   /**     * Return the number of items in the queue.     */    public int size() {        return N;         }   /**     * Return the number of items in the queue.     */    public int length() {        return N;         }   /**     * Return the item least recently added to the queue.     * Throw an exception if the queue is empty.     */    public Item peek() {        if (isEmpty()) throw new RuntimeException("Queue underflow");        return first.item;    }   /**     * Add the item to the queue.     */    public void enqueue(Item item) {        Node x = new Node();        x.item = item;        if (isEmpty()) { first = x;     last = x; }        else           { last.next = x; last = x; }        N++;    }   /**     * Remove and return the item on the queue least recently added.     * Throw an exception if the queue is empty.     */    public Item dequeue() {        if (isEmpty()) throw new RuntimeException("Queue underflow");        Item item = first.item;        first = first.next;        N--;        if (isEmpty()) last = null;   // to avoid loitering        return item;    }   /**     * Return string representation.     */    public String toString() {        StringBuilder s = new StringBuilder();        for (Item item : this)            s.append(item + " ");        return s.toString();    }     /**     * Return an iterator that iterates over the items on the queue in FIFO order.     */    public Iterator<Item> iterator()  {        return new ListIterator();      }    // an iterator, doesn't implement remove() since it's optional    private class ListIterator implements Iterator<Item> {        private Node current = first;        public boolean hasNext()  { return current != null;                     }        public void remove()      { throw new UnsupportedOperationException();  }        public Item next() {            if (!hasNext()) throw new NoSuchElementException();            Item item = current.item;            current = current.next;             return item;        }    }   /**     * A test client.     */    public static void main(String[] args) {        Queue<String> q = new Queue<String>();        while (!StdIn.isEmpty()) {            String item = StdIn.readString();            if (!item.equals("-")) q.enqueue(item);            else if (!q.isEmpty()) StdOut.print(q.dequeue() + " ");        }        StdOut.println("(" + q.size() + " left on queue)");    }}


  • Array implementation. Similar to array implementation of stack, but a little trickier since need to wrap-around. Program DoublingQueue.java implements the queue interface. The array is dynamically resized using repeated doubling.
import java.util.Iterator;import java.util.NoSuchElementException;public class DoublingQueue<Item> implements Iterable<Item> {    private Item[] q;            // queue elements    private int N = 0;           // number of elements on queue    private int first = 0;       // index of first element of queue    private int last  = 0;       // index of next available slot    // cast needed since no generic array creation in Java    public DoublingQueue() {        q = (Item[]) new Object[2];    }    public boolean isEmpty() { return N == 0;    }    public int size()        { return N;         }    // resize the underlying array    private void resize(int max) {        assert(max >= N);        Item[] temp = (Item[]) new Object[max];        for (int i = 0; i < N; i++) temp[i] = q[(first + i) % q.length];        q = temp;        first = 0;        last  = N;    }    public void enqueue(Item item) {        // double size of array if necessary and recopy to front of array        if (N == q.length) resize(2*q.length);   // double size of array if necessary        q[last++] = item;                        // add item        if (last == q.length) last = 0;          // wrap-around        N++;    }    // remove the least recently added item     public Item dequeue() {        if (isEmpty()) throw new RuntimeException("Queue underflow");        Item item = q[first];        q[first] = null;                            // to avoid loitering        N--;        first++;        if (first == q.length) first = 0;           // wrap-around        // shrink size of array if necessary        if (N > 0 && N == q.length/4) resize(q.length/2);         return item;    }    public Iterator<Item> iterator() { return new QueueIterator(); }    // an iterator, doesn't implement remove() since it's optional    private class QueueIterator implements Iterator<Item> {        private int i = 0;        public boolean hasNext()  { return i < N;                               }        public void remove()      { throw new UnsupportedOperationException();  }        public Item next() {            if (!hasNext()) throw new NoSuchElementException();            Item item = q[(i + first) % q.length];            i++;            return item;        }    }    // a test client    public static void main(String[] args) {        DoublingQueue<String> queue = new DoublingQueue<String>();        queue.enqueue("Delete");        queue.enqueue("This");        queue.enqueue("is");        queue.enqueue("a");        queue.enqueue("test.");        queue.dequeue();        for (String s : queue)            System.out.println(s);        System.out.println();        while (!queue.isEmpty())  {            System.out.println(queue.dequeue());        }    }}


Doubly linked list


Doubly-linked-list.svg

Open doubly-linked lists

record DoublyLinkedNode {    prev // A reference to the previous node    next // A reference to the next node    data // Data or a reference to data }
record DoublyLinkedList {     DoublyLinkedNode firstNode   // points to first node of list      DoublyLinkedNode lastNode    // points to last node of list }

Traversing the list

Traversal of a doubly-linked list can be in either direction. In fact, the direction of traversal can change many times, if desired. Traversal is often called iteration, but that choice of terminology is unfortunate, for iteration has well-defined semantics (e.g., in mathematics) which are not analogous to traversal.

Forwards

node  := list.firstNode while node ≠ null     <do something with node.data>     node  := node.next

Backwards

node  := list.lastNode while node ≠ null     <do something with node.data>     node  := node.prev

Inserting a node

These symmetric functions insert a node either after or before a given node:

function insertAfter(List list, Node node, Node newNode)     newNode.prev  := node     newNode.next  := node.next     if node.next == null         list.lastNode  := newNode     else         node.next.prev  := newNode     node.next  := newNode
function insertBefore(List list, Node node, Node newNode)     newNode.prev  := node.prev     newNode.next  := node     if node.prev == null         list.firstNode  := newNode     else         node.prev.next  := newNode     node.prev  := newNode

We also need a function to insert a node at the beginning of a possibly empty list:

function insertBeginning(List list, Node newNode)     if list.firstNode == null         list.firstNode  := newNode         list.lastNode   := newNode         newNode.prev  := null         newNode.next  := null     else         insertBefore(list, list.firstNode, newNode)

A symmetric function inserts at the end:

function insertEnd(List list, Node newNode)     if list.lastNode == null         insertBeginning(list, newNode)     else         insertAfter(list, list.lastNode, newNode)

Removing a node

Removal of a node is easier than insertion, but requires special handling if the node to be removed is the firstNode or lastNode:

function remove(List list, Node node)   if node.prev == null       list.firstNode  := node.next   else       node.prev.next  := node.next   if node.next == null       list.lastNode  := node.prev   else       node.next.prev  := node.prev   destroy node

java代码实现
import java.util.ListIterator;import java.util.NoSuchElementException;public class DoublyLinkedList<Item> implements Iterable<Item> {    private int N;        // number of elements on list    private Node pre;     // sentinel before first item    private Node post;    // sentinel after last item    public DoublyLinkedList() {        pre  = new Node();        post = new Node();        pre.next = post;        post.prev = pre;    }    // linked list node helper data type    private class Node {        private Item item;        private Node next;        private Node prev;    }    public boolean isEmpty()    { return N == 0; }    public int size()           { return N;      }    // add the item to the list    public void add(Item item) {        Node last = post.prev;        Node x = new Node();        x.item = item;        x.next = post;        x.prev = last;        post.prev = x;        last.next = x;        N++;    }    public ListIterator<Item> iterator()  { return new DoublyLinkedListIterator(); }    // assumes no calls to DoublyLinkedList.add() during iteration    private class DoublyLinkedListIterator implements ListIterator<Item> {        private Node current      = pre.next;  // the node that is returned by next()        private Node lastAccessed = null;      // the last node to be returned by prev() or next()                                               // reset to null upon intervening remove() or add()        private int index = 0;        public boolean hasNext()      { return index < N; }        public boolean hasPrevious()  { return index > 0; }        public int previousIndex()    { return index - 1; }        public int nextIndex()        { return index;     }        public Item next() {            if (!hasNext()) throw new NoSuchElementException();            lastAccessed = current;            Item item = current.item;            current = current.next;             index++;            return item;        }        public Item previous() {            if (!hasPrevious()) throw new NoSuchElementException();            current = current.prev;            index--;            lastAccessed = current;            return current.item;        }        // replace the item of the element that was last accessed by next() or previous()        // condition: no calls to remove() or add() after last call to next() or previous()        public void set(Item item) {            if (lastAccessed == null) throw new IllegalStateException();            lastAccessed.item = item;        }        // remove the element that was last accessed by next() or previous()        // condition: no calls to remove() or add() after last call to next() or previous()        public void remove() {             if (lastAccessed == null) throw new IllegalStateException();            Node x = lastAccessed.prev;            Node y = lastAccessed.next;            x.next = y;            y.prev = x;            N--;            if (current == lastAccessed)                current = y;            else                index--;            lastAccessed = null;        }        // add element to list         public void add(Item item) {            Node x = current.prev;            Node y = new Node();            Node z = current;            y.item = item;            x.next = y;            y.next = z;            z.prev = y;            y.prev = x;            N++;            index++;            lastAccessed = null;        }    }    public String toString() {        StringBuilder s = new StringBuilder();        for (Item item : this)            s.append(item + " ");        return s.toString();    }    // a test client    public static void main(String[] args) {        int N  = Integer.parseInt(args[0]);        // add elements 1, ..., N        StdOut.println(N + " random integers between 0 and 99");        DoublyLinkedList<Integer> list = new DoublyLinkedList<Integer>();        for (int i = 0; i < N; i++)            list.add((int) (100 * Math.random()));        StdOut.println(list);        StdOut.println();        ListIterator<Integer> iterator = list.iterator();        // go forwards with next() and set()        StdOut.println("add 1 to each element via next() and set()");        while (iterator.hasNext()) {            int x = iterator.next();            iterator.set(x + 1);        }        StdOut.println(list);        StdOut.println();        // go backwards with previous() and set()        StdOut.println("multiply each element by 3 via previous() and set()");        while (iterator.hasPrevious()) {            int x = iterator.previous();            iterator.set(x + x + x);        }        StdOut.println(list);        StdOut.println();        // remove all elements that are multiples of 4 via next() and remove()        StdOut.println("remove elements that are a multiple of 4 via next() and remove()");        while (iterator.hasNext()) {            int x = iterator.next();            if (x % 4 == 0) iterator.remove();        }        StdOut.println(list);        StdOut.println();        // remove all even elements via previous() and remove()        StdOut.println("remove elements that are even via previous() and remove()");        while (iterator.hasPrevious()) {            int x = iterator.previous();            if (x % 2 == 0) iterator.remove();        }        StdOut.println(list);        StdOut.println();        // add elements via next() and add()        StdOut.println("add elements via next() and add()");        while (iterator.hasNext()) {            int x = iterator.next();            iterator.add(x + 1);        }        StdOut.println(list);        StdOut.println();        // add elements via previous() and add()        StdOut.println("add elements via previous() and add()");        while (iterator.hasPrevious()) {            int x = iterator.previous();            iterator.add(x * 10);            iterator.previous();        }        StdOut.println(list);        StdOut.println();    }}


References:

http://introcs.cs.princeton.edu/java/43stack/

http://en.wikibooks.org/wiki/Data_Structures/Stacks_and_Queues

http://en.wikipedia.org/wiki/Doubly_linked_list

0 0