用數組和鏈表實現自制的棧和隊列

數組和鏈表是常用的兩種數據結構,在翻看了Stack類,Iterable接口,Iterator接口,Queue的Java源碼后,嘗試用數組和鏈表來自己實現一下棧和隊列。

自己造過輪子后,實實在在的理解了輪子,才能真正深入的使用輪子。代碼如下:

說明:Stackable是我自己寫的接口

數組實現棧

public class SugarArrayStack<E> implements Stackable<E> ,Iterable<E>{

private static final int DEFAULT_CAPACITY = 2;

private int N = 0;
private E[] a;


public SugarArrayStack() {
    resize(DEFAULT_CAPACITY);
}

public SugarArrayStack(int size){
    resize(size);
}

private void resize(int size){
    E[] temp = (E[])new Object[size];
    for(int i = 0; i < N; i++){
        temp[i] = a[i];
    }
    a = temp;
}

@Override
public void push(E e){
    if(N >= a.length - 1){
        resize(a.length * 2);
    }
    a[N++] = e;
}

@Override
public E pop(){
    E temp = peek();
    a[--N] = null;
    if(N >= 0 && N == a.length / 4){
        resize(a.length / 2);
    }
    return temp;
}

@Override
public E peek(){
    int index = N - 1;
    return a[index];
}

@Override
public int size(){
    return N;
}

@Override
public boolean isEmpty(){
    return N == 0;
}


@Override
public Iterator<E> iterator() {
    return new ArrayIterator(N);
}

private class ArrayIterator implements Iterator<E>{

    private int currentIndex;

    public ArrayIterator(int currentIndex) {
        this.currentIndex = currentIndex;
    }

    @Override
    public boolean hasNext() {
        return currentIndex > 0;
    }

    @Override
    public E next() {
        currentIndex--;
        return a[currentIndex];
    }
}
}

鏈表實現棧隊列

public class SugarLinkedQueue<E> implements Iterable<E>{

private Node<E> mHeadNode;
private Node<E> mTailNode;
private int mCount = 0;

public void enqueue(E e){
    Node<E> newTailNode = new Node<>(e,null);
    if(mCount == 0){
        mTailNode = newTailNode;
        mHeadNode = newTailNode;
    }else{
        Node<E> oldTailNode = mTailNode;
        mTailNode = newTailNode;
        oldTailNode.nextNode = newTailNode;
    }
    mCount ++;
}

public E dequeue(){
    if(isEmpty()){
        throw new IllegalStateException("Please enqueue an element firstly");
    }
    mCount --;

    E tempE = mHeadNode.e;
    mHeadNode = mHeadNode.nextNode;
    return tempE;
}

public int size(){
    return mCount;
}

public boolean isEmpty(){
    return mCount == 0;
}

@Override
public Iterator<E> iterator() {
    return new QueueIterator(mHeadNode);
}

private class QueueIterator implements Iterator<E>{

    private Node<E> currentNode;

    public QueueIterator(Node<E> currentNode) {
        this.currentNode = currentNode;
    }

    @Override
    public boolean hasNext() {
        return currentNode != null;
    }

    @Override
    public E next() {
        E currentE = currentNode.e;
        currentNode = currentNode.nextNode;
        return currentE;
    }
}

private class Node<E>{
    private E e;
    private Node<E> nextNode;
    public Node(E e, Node<E> nextNode) {
        this.e = e;
        this.nextNode = nextNode;
    }
}

}

鏈表實現棧

public class SugarLinkedStack<E> implements Stackable<E> ,Iterable<E>{

private Node<E> headNode;
private int count = 0;

@Override
public boolean isEmpty() {
    return count == 0;
}

@Override
public int size() {
    return count;
}

@Override
public void push(E e) {
    count++;
    headNode = new Node<>(e, headNode);
}

@Override
public E pop() {
    if(count <= 0){
        throw new IllegalStateException("please push an element firstly");
    }
    count --;

    E e = headNode.e;
    headNode = headNode.nextNode;
    return e;
}

@Override
public E peek() {
    return headNode.e;
}

@Override
public Iterator<E> iterator() {
    return new LinkedIterator(headNode);
}

private class LinkedIterator implements Iterator<E>{

    private Node<E> currentNode;

    public LinkedIterator(Node<E> currentNode) {
        this.currentNode = currentNode;
    }

    @Override
    public boolean hasNext() {
        return currentNode != null;
    }

    @Override
    public E next() {
        E tempE = currentNode.e;
        currentNode = currentNode.nextNode;
        return tempE;
    }
}

private class Node<E> {
    private E e;
    private Node<E> nextNode;

    private Node(E e, Node<E> nextNode) {
        this.e = e;
        this.nextNode = nextNode;
    }
}
}

小結

在實現過程中,產生了一些心得和感受,如下:

  1. 新建一個Java類,順序是:先初始化成員變量,然后再調用它的構造方法。因此,某些場景上,一些操作是放在成員變量上還是放在構造方法里是有選擇和考量的
  2. 鏈表這種數據結構天生包含迭代的編程思想,用遞歸來解決遇到的問題
  3. 數組這種數據結構使用索引訪問元素(指針的使用感),操作數組序號來解決遇到的問題
  4. 用鏈表的實現,插入和刪除元素的操作和元素數量多少沒有關系。
  5. 深刻理解Iterator遍歷原理,這個接口提供了集合遍歷的邏輯封裝,邏輯分為兩個部分:hasNext方法和next方法。這兩個方法合在一起產生出遞歸的效果,從而實現集合遍歷

自制棧實踐

是時候使用自己造的輪子了

問題表述:給出一個字符串的算術表達式,求其值。比如“((( 10 + (34 - 24))*5)-(200/11))”

思路分析:用兩個棧,一個保存運算符,一個保存操作數。將操作數壓入操作數棧,將運算符壓入運算符棧,忽略左括號,每遇到右括號,彈出一個運算符,彈出所需數量的操作數,計算后所得結果再壓入操作數棧。

代碼如下:

public static void main(String[] args) {
    String expression = "((( 10 + (34 - 24))*5)-(200/11))";
    float result = calculate(expression);
    System.out.println("result = "+result);
}

private static float calculate(String expression) {
    if (expression != null && expression.length() > 0) {
        Stackable<Character> operatorsStack = new SugarArrayStack<>();
        Stackable<Float> valueStack = new SugarLinkedStack<>();
        StringBuilder sb = new StringBuilder();

        char[] charArray = expression.toCharArray();
        for (char c : charArray) {
            switch (c) {
                case '(':
                case ' ':
                    break;
                case '+':
                case '-':
                case '*':
                case '/':
                    pushNumberValue(valueStack, sb);
                    operatorsStack.push(c);
                    break;
                case ')':
                    pushNumberValue(valueStack, sb);

                    Float firstPop = valueStack.pop();
                    Character operation = operatorsStack.pop();
                    float f;
                    switch (operation) {
                        case '+':
                            f = valueStack.pop() + firstPop;
                            break;
                        case '-':
                            f = valueStack.pop() - firstPop;
                            break;
                        case '*':
                            f = valueStack.pop() * firstPop;
                            break;
                        case '/':
                            f = valueStack.pop() / firstPop;
                            break;
                        default:
                            throw new IllegalArgumentException("this operator haven't been supported now");
                    }
                    valueStack.push(f);
                    break;
                default:
                    sb.append(c);
            }
        }

        return valueStack.peek();
    }

    return -1;
}


private static void pushNumberValue(Stackable<Float> valueStack, StringBuilder sb) {
    int length = sb.toString().length();
    if(length > 0){
        float value = Float.parseFloat(sb.toString());
        valueStack.push(value);
        sb.delete(0, length);
    }
}

如上例子,最終輸出的結果是:

result = 81.818184

這種計算存在一些缺陷:

  • 需要左右括號包裝算術表達式
  • 在一個括號里,只能進行兩個操作數的計算

小結

到此,我們用自己造的棧,實現了任意長度下的四則算術運算,今天的天氣也比昨天格外得好了。

參考資料

  1. 算法 Algorithms Fourth Edition By Robert Sedgewick & Kevin Wayne
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,533評論 1 51
  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區別 13、...
    Miley_MOJIE閱讀 3,729評論 0 11
  • 我相信 愛的本質一如 生命的單純與溫柔 我相信 所有的 光與影的反射和相投 我相信 滿樹的花朵 只源于冰雪中的一粒...
    采蘭格格閱讀 159評論 0 0
  • 昨晚的比較直白的被拒絕,心里沒有什么感覺。好像這是應該的,我知道我并不是特別喜歡這個人。之前有過放棄追求,這么久一...
    天青色_250d閱讀 129評論 0 0
  • 春來秋往,不顧疲倦,沒有憂傷,冥冥之中有一種力量,抑或說是自然的規律,倒不如說是信仰,燕如人,人亦如燕
    行走的老貓閱讀 276評論 0 3