《算法》- 隊列和棧

一、隊列: 先進先出(FIFO),例如超市的收銀臺、排隊買票的顧客。在Java中,它和List的區別在於,List能夠在任意位置添加和刪除元素,而Queue只有兩個操做:java

  • 把元素添加到隊列末尾;
  • 從隊列頭部取出元素。

二、棧: 下壓棧,後進先出(LIFO),例如你辦公桌上的一疊信件,新信件來時將它們放在最上面(push方法),當閱讀時從上到下取件(pop方法)。node

三、雙棧算術表達式求值:
例如計算(1+((2+3)*(4*5)))的值:用兩個棧,一個保存運算符(運算符棧),一個保存數字(操做數棧)。算法

從左到右逐個將實體送入棧處理:
一、遇到數字時,將數字壓入操做數棧,遇到運算法時,壓入運算符棧;
二、遇到左括號時忽略;
三、遇到右括號,彈出一個運算符,彈出所需數量的數字,並將運算符和數字的運算結果壓入操做數棧。數組

算法演示

雙棧算術表達式求值算法(爲了代碼簡潔未考慮異常):數據結構

import java.util.Stack;

public class EvaluateTest {
    public static void main(String[] args) {
        // 須要計算的表達式
        String str = "(1+((2+3)*(4*5)))".trim();

        // 運算符棧和操做數棧
        Stack<String> ops = new Stack<>();
        Stack<Double> vals = new Stack<>();

        for(int i=0;i < str.length();i++) {
            String s = String.valueOf(str.charAt(i));
            if(s.equals("(")){
                // 左括號時忽略
            }else if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/") || s.equals("sqrt")){
                // 運算符時壓運算符棧
                ops.push(s);
            }else if(s.equals(")")){
                // 右括號時,將兩個棧都pop,再計算、壓棧
                String op = ops.pop();
                Double v = vals.pop();
                if(op.equals("+")){
                    v = vals.pop() + v;
                }else if(op.equals("-")){
                    v = vals.pop() - v;
                }if(op.equals("*")){
                    v = vals.pop() * v;
                }if(op.equals("/")){
                    v = vals.pop() / v;
                }if(op.equals("sqrt")){
                    v = Math.sqrt(v);
                }
                vals.push(v);
            }else{
                // 最後是數字時,轉爲double壓入操做數棧
                vals.push(Double.valueOf(s));
            }
        }
        System.out.println("最終運算結果:" + vals.pop());
    }
}

四、鏈表
鏈表是一種遞歸的數據結構,它能夠爲空(null),能夠是指向一個節點(node)的引用。該節點包含一個元素(數據域,儲存節點含有的信息)和一個指向另外一條鏈表或節點的引用(引用域)。lua

鏈表的特色:3d

  • 插入和刪除元素方便;
  • 查找數據時效率低,訪問某個位置的數據要從第一個節點開始訪問,根據第一個節點保存的下一個節點的地址找到第二個節點,以此類推;

能夠看完下面的流程再來理解它的特色。這裏主要介紹單向鏈表:code

// 一個節點
public class Node {
    public Object item;
    public Node next;
}

僞代碼構造一條鏈表:
對象

建立好鏈表後,在表頭插入一個節點很容易,以下圖,若是在表頭插入字符串"not",先將first保存在oldfirst中,而後將一個新節點賦給first,並將它的item元素設爲not,next設爲oldfirst。
blog

在表頭刪除一個節點,將first指向first.next便可。曾經的第一個節點對象變成了一個孤兒,Java的內存管理最終將回收它所佔用的內存:

請注意:當鏈表中只有一個節點時,它既是首節點又是尾節點,另外注意鏈表爲空的狀況。

那麼在表尾插入節點能夠表示爲:

之前鏈表操做只須要幾行賦值代碼,所需時間和鏈表的長度無關。但若是須要刪除表尾節點,就要遍歷整條鏈表並找出指向last節點的節點,這樣所需的時間和鏈表的長度成正比。
要想實現任意插入和刪除操做,可使用雙向鏈表,這裏不做介紹。

五、遍歷鏈表 咱們知道遍歷數組能夠用for(int i = 0; i < N; i++){...};那麼遍歷鏈表也有一個對應方式:

for (Node x = first; x != null; x = x.next) {
   // 處理 x.item
}

六、堆棧的鏈表實現
直接上代碼:

import java.util.Iterator;
import java.util.NoSuchElementException;

public class Stack<Item> implements Iterable<Item> {
    private Node<Item> first;     // 棧頂(最近添加的元素)
    private int n;                // 元素數量

    private static class Node<Item> {
        private Item item;
        private Node<Item> next;// 定義了節點的嵌套類
    }

    /**
     * Initializes an empty stack.
     */
    public Stack() {
        first = null;
        n = 0;
    }

    /**
     * 當first==null或者n==0時,棧是空的
     */
    public boolean isEmpty() {
        return first == null;
    }
    
    public int size() {
        return n;
    }

    /**
     * 向棧頂添加元素
     */
    public void push(Item item) {
        Node<Item> oldfirst = first;
        first = new Node<Item>();
        first.item = item;
        first.next = oldfirst;
        n++;
    }

    /**
     * 從棧頂刪除元素
     */
    public Item pop() {
        if (isEmpty()) throw new NoSuchElementException("Stack underflow");
        Item item = first.item;
        first = first.next;
        n--;
        return item;
    }


    /**
     * 只取值,不刪除
     */
    public Item peek() {
        if (isEmpty()) throw new NoSuchElementException("Stack underflow");
        return first.item;
    }

    /**
     * 按照LIFO的順序,返回一個迭代器能夠迭代此類
     */
    public Iterator<Item> iterator() {
        return new LinkedIterator(first);
    }
    
    private class LinkedIterator implements Iterator<Item> {
        private Node<Item> current;

        public LinkedIterator(Node<Item> first) {
            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;
        }
    }
}

七、隊列的鏈表實現
Queue的實現使用的數據結構和Stack類相同,都是鏈表,但它實現了不一樣的添加和刪除算法,這也是FIFO和LIFO的區別所在。

import java.util.Iterator;
import java.util.NoSuchElementException;

public class Queue<Item> implements Iterable<Item> {
    private Node<Item> first;    // 指向最先添加的節點的引用
    private Node<Item> last;     // 隊尾,最近添加
    private int n;

    private static class Node<Item> {
        private Item item;
        private Node<Item> next;
    }

    public Queue() {
        first = null;
        last  = null;
        n = 0;
    }

    public boolean isEmpty() {
        return first == null;
    }

    public int size() {
        return n;
    }

    /**
     * 向表尾添加元素
     */
    public void enqueue(Item item) {
        Node<Item> oldlast = last;
        last = new Node<Item>();
        last.item = item;
        last.next = null;
        if (isEmpty()){
            first = last;
        }else{
            oldlast.next = last;
        }
        n++;
    }

    /**
     * 從表頭刪除元素
     */
    public Item dequeue() {
        if (isEmpty()) throw new NoSuchElementException("Queue underflow");
        Item item = first.item;
        first = first.next;
        n--;
        if (isEmpty()) last = null;
        return item;
    }

    /**
     * 從表頭獲取元素,不刪除
     */
    public Item peek() {
        if (isEmpty()) throw new NoSuchElementException("Queue underflow");
        return first.item;
    }

    public Iterator<Item> iterator()  {
        return new LinkedIterator(first);
    }

    private class LinkedIterator implements Iterator<Item> {
        private Node<Item> current;

        public LinkedIterator(Node<Item> first) {
            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;
        }
    }
}

相關文獻:
Bags, Queues, and Stacks

相關文章
相關標籤/搜索