[從今天開始修煉數據結構]棧、斐波那契數列、逆波蘭四則運算的實現

1、棧的定義java

  棧是限定僅在表尾進行插入和刪除操做的線性表。容許插入和刪除的一端稱爲棧頂(top),另外一端稱爲棧底(bottom)。棧又稱後進先出的線性表,簡稱LIFO結構。數組

  注意:首先它是一個線性表,也就是說棧元素有前驅後繼關係。函數

  棧的插入操做,叫作進棧,也稱壓棧、入棧this

  棧的刪除操做,叫作出棧,也叫彈棧。spa

  注意:最早入棧,不表明就要最後出棧。由於棧沒有限制出棧的時間,例如能夠先入棧兩個元素,再出棧兩個元素,後入棧其餘元素。.net

2、棧的抽象數據類型3d

  

ADT Stack
Data
    同線性表。元素具備相同的類型,相鄰元素具備前驅和後繼。
Operation
    InitStack(S) : 初始化操做,創建一個空棧
    DestroyStack(S):若棧存在,銷燬它。
    ClearStack(S):將棧清空
    StackEmpty:若棧爲空,返回true,不然返回false。
    GetTop(S,e):若棧存在且非空,用e返回棧頂元素。
    Push(S,e):若棧S存在,插入新元素e到棧頂
    Pop(S,e):刪除S中棧頂元素,並用e返回其值
    StackLength(S):返回棧S中的元素個數
endADT

3、棧的順序結構code

1,棧的順序存儲結構是什麼樣子blog

  棧的順序存儲結構也是用數組來實現的。讓下標0的一端做爲棧底,另外一端做爲棧頂。若存儲棧的長度爲StackSize,則棧頂位置top必須小於StackSize。當棧存在一個元素時,top = 0.所以一般把空棧的斷定條件定爲top = - 1。遞歸

2,順序結構棧的實現

package Stack;

public class ArrayStack<T>{
    private Object[] list;
    private int top;
    private int size;
    private static int DEFAULT_SIZE = 10;

    public ArrayStack(int size){
        list = new Object[size];
        this.size = size;
        top = -1;
    }

    public ArrayStack(){
        this(DEFAULT_SIZE);
    }

    public void push(T data){

        if (!isFull()){
                list[++top] = data;
        }else {
            System.out.println("棧滿了");
        }
    }

    public T pop(){
        if (!isEmpty()){
                T obj = (T)list[top];
                list[top] = null;
                top--;
                return obj;

        }else {
            System.out.println("棧是空的");
        }
        return null;
    }

    private boolean isEmpty(){
        return top == -1;
    }

    private boolean isFull() {
        return top >= size;
    }

    public int size(){
        return size;
    }

    public int getTop(){
        return top;
    }
}

 4、共享棧

  1,共享棧是指兩棧共享一片空間的棧。以下圖所示

 

 

   兩個棧共用一塊數組,以兩頭爲棧底,中間爲棧頂。能夠節省空間。判斷滿的方法是top1 + 1 = top2.

 

  2,共享棧的實現。

package Stack;

public class ShareStack<T> {
    private Object[] list;
    private int top1;
    private int top2;
    private int capacity;
    private static int DEFAULT_SIZE = 10;
    //private boolean isFull = false;

    public ShareStack(int capacity){
        list = new Object[capacity];
        this.capacity = capacity;
        top1 = -1;
        top2 = capacity;
    }

    public ShareStack(){
        this(DEFAULT_SIZE);
    }

    public boolean pushStackOne(T data){
        if (!isFull()){
            list[++top1] = data;
            //isFull = isFull();
            return true;
        }else{
            System.out.print("棧滿了!");
            return false;
        }
    }

    public boolean pushStackTwo(T data){
        if (!isFull()){
            list[--top2] = data;
            //isFull = isFull();
            return true;
        }else{
            System.out.print("棧滿了!");
            return false;
        }
    }

    public T popStackOne(){
        if (!isEmptyOne()){
            T data = (T)list[top1--];
            return data;
        }else {
            System.out.println("棧一是空的!");
            return null;
        }
    }

    public T popStackTwo(){
        if (!isEmptyTwo()){
            T data = (T)list[top2++];
            return data;
        }else {
            System.out.println("棧二是空的!");
            return null;
        }
    }

    private boolean isEmptyTwo() {
        return top2 == capacity;
    }

    private boolean isFull() {
        return top1 == (top2 - 1);
    }

    private boolean isEmptyOne(){
        return top1 == -1;
    }

}

5、棧的鏈式存儲及實現 

  1,棧的鏈式存儲簡稱鏈棧,把棧頂放在單鏈表的頭部。以下圖

 

 

 2,鏈棧的實現

public class LinkedStack<T> {
    private Node<T> top;

    public void push(T data){
        Node<T> newNode = new Node<>(data, null);
        Node<T> t = top;
        top = newNode;
        top.next = t;
    }

    public T pop(){
        Node<T> t = top;
        if (!isEmpty()){
            top = top.next;
            return t.data;
        }else {
            System.out.println("棧爲空!");
            return null;
        }
    }

    private boolean isEmpty() {
        return top == null;
    }

    private class Node<T> {
        private T data;
        private Node next;

        public Node(T data, Node<T> next){
            this.data = data;
            this.next = next;
        }
    }

}

以表頭爲棧頂,下降了彈棧操做的時間複雜度。若以表尾爲棧頂則彈棧只能遍歷找到top-1位置,或者用雙向鏈表犧牲空間。以下

 public T pop(){
        if (!isEmpty()){
            Node<T> newTop = bottom;
            while (newTop.next != top){
                newTop = newTop.next;
            }
            T data = top.data;
            top = newTop;
            top.next = null;
            return data;
        }else {
            System.out.println("棧是空的!");
            return null;
        }
    }

5、棧的應用 —— 遞歸

1,斐波那契數列的遞歸實現

  引例:若是兔子在出生兩個月後就有繁殖能力,一對兔子每月能生出一對小兔子。假設全部兔子都不死,那麼一年後有多少對兔子?分析得出兔子數量按天數增長以下表

所通過的月數 1 2 3 4 5 6 7 8 9 10 11 12
兔子對數 1 1 2 3 5 8 13 21 34 55 89 144

表中數列有明顯的特色:相鄰前兩項之和等於後一項。抽象成函數表達以下

 

 

 迭代實現以下:

public class Fibonacci {
public static void main(String[] args) throws Exception {
System.out.println(Fibonacci(12));
}

public static int Fibonacci(int month) throws Exception {
if (month < 0){
throw new Exception();
}
if (month < 2){
return month == 0 ? 0 : 1;
}else {
return (Fibonacci(month - 1) + Fibonacci(month - 2));
}
}
}

代碼執行過程以下:

  

 

遞歸實現法的時間複雜度爲O(2n),過高,還可能會引發內存棧溢出,因此應該用別的方法求解。這篇文章主要講由棧引出遞歸,因此先挖個坑,在後面的文章中我會單獨討論遞歸和斐波那契數列的其餘解法。

參考:https://blog.csdn.net/sofia_m/article/details/78796084

https://blog.csdn.net/IronWring_Fly/article/details/100050016

https://blog.csdn.net/Bob__yuan/article/details/84956740

2,在前行階段,對於每一層遞歸,函數的局部變量、參數值以及返回地址都被壓入棧中。在退回階段,位於棧頂的局部變量、參數值和返回地址被彈出,用於返回調用層次中執行代碼的其他部分,也就是恢復了調用的狀態。

6、棧的應用 —— 四則運算表達式求值

1,後綴(逆波蘭)表示法定義

  對於會計計算器,不能表達複雜的帶括號和多種運算符號複合的表達式;而對於科學計算器,咱們能夠一次將一個複雜的帶括號的四則運算輸入進去,計算器是怎麼作到的呢?

  這就引入了一種不須要括號的後綴表達法 —— 逆波蘭表達法。舉個例子來看 對於 「 9 + ( 3 - 1) * 3 + 10 / 2」這個表達式,轉換成後綴表達式應該變爲 「 9 3 1 - 3 * + 10 2 / + 」。 叫作後綴的緣由就是全部的符號都在被運算的數字後面出現。

2,咱們先來看看後綴表達式是如何計算的 

  規則: 從左到右遍歷表達式的每一個數字和符號,遇到數字就進棧,遇到符號,就把數字棧棧頂的兩個數字出棧,棧頂元素放在運算符後面,棧頂下面一個元素放在運算符前面。再將運算結果進棧,如此往復,直到得到最終結果。

  步驟精解:咱們以上面的 「 9 3 1 - 3 * + 10 2 / + 」 爲例

  (1)初始化一個空棧,用來對要運算的數字進行進出使用

  (2)後綴表達式中前三個都是數字,因此將9 3 1 依次進棧。

 

 

   (3)接下來是運算符「 - 」,因此將1, 3 出棧,運算3 - 1獲得2 ,將2進棧,而後遇到3 ,將3進棧

 

 

   (4)後面遇到「 * 」,將3, 2出棧,計算2 * 3,獲得6,將6進棧

 

 

  (5)遇到「 + 」,將6 , 9出棧,計算9 + 6,獲得15,將15進棧

 

 

   (6)將10, 2 進棧

 

 

   (7)遇到符號「 / 」 將2, 10出棧,計算10/2獲得5,將5壓棧

 

   (8)最後一個符號 「 + 」,將15,5出棧,計算15 + 5,獲得20,將20壓棧

 

   (9)表達式讀取完成,將結果20出棧,棧變爲空。

這就是經過後綴表達式計算四則運算的結果。那麼下面咱們再來討論如何將中綴表達式(也就是咱們平時數學課上計算的形式)轉換爲方便計算機運算的後綴表達式呢?

3,中綴轉後綴

  規則:從左到右遍歷中綴表達式,若是是數字就存入後綴表達式,若是是符號就判斷其與棧頂符號的優先級,若是優先級不高於棧頂符號,則棧頂元素依次出棧並輸出,並將當前符號進棧。特殊的,遇到左括號就進棧,左括號的優先級不作判斷;當遇到右括號時,將與其匹配的左括號以上的所有符號依次出棧。直到最終輸出後綴表達式爲止。

  具體步驟:咱們仍是以上面的 「 9 + ( 3 - 1) * 3 + 10 / 2」這個表達式爲例

  (1)初始化一個空棧。遇到第一個字符是數字9,輸出9,遇到符號「 + 」,進棧。

 

   (2)遇到字符「 ( 」進棧,而後遇到了數字3,輸出,又遇到了符號「 - 」,進棧。以下圖

 

   (3)遇到符號「  ) 」,根據規則,匹配前面的「 ( 」,並將其上的符號依次出棧並輸出,直到「 ( 」出棧。

 

   (4)下面遇到了符號「 * 」,比較其與棧頂元素 「 + 」的優先級。* 的優先級更高,因此壓棧。 後面遇到了數字3 ,輸出。

 

   (5)以後遇到了符號 「 + 」,與棧頂元素「 * 」比較, + 的優先級低,因此將 * 彈棧,再與如今的棧頂 + 比較,與新來的 + 優先級相同,因此將棧頂+也出棧,將新來的 + 彈棧。以下圖

 

   (6)緊接着數字10,輸出,後面是符號「 / 」,進棧。 最後一個數字2 輸出。原表達式讀取完成,將棧內剩餘的符號都出棧,獲得

 

 4,實現逆波蘭四則運算(靜態方法調用)

package Stack;

import java.util.Stack;
import java.util.regex.Pattern;

public class RPNmethod {
    private static Stack<String> charStack = new Stack<>();
    private static Stack<Integer> intStack = new Stack<>();

    public static int RPN(String exp){
        String behindEXP = convert(exp);
        String[] chars = behindEXP.split(" ");
        for (int i = 0; i < chars.length; i++){
            String thisOne = chars[i];
            if (isNum(thisOne)){
                intStack.push(Integer.parseInt(thisOne));
            }else if (isSymbol(thisOne)){
                int a = intStack.pop();
                int b = intStack.pop();
                int result;
                switch (thisOne){
                    case "+": result = b + a;
                        intStack.push(result);
                        break;
                    case "-": result = b - a;
                        intStack.push(result);
                        break;
                    case "*": result = b * a;
                        intStack.push(result);
                        break;
                    case "/": result = b / a;
                        intStack.push(result);
                        break;
                }
            }
        }
        return intStack.pop();
    }

    /**
     * 將中綴表達式轉爲後綴表達式
     * @param middle
     * @return
     */
    private static String convert(String middle){
        String rpnEXP = "";
        String[] chars = middle.split(" ");
        for(int i = 0; i < chars.length; i++){
            String thisOne = chars[i];
            if (isNum(thisOne)){
                rpnEXP = rpnEXP + thisOne + " ";
            }else if (isSymbol(thisOne)) {
                /*
                三個分支 : 是左括號,是右括號,不是括號
                 */
                if (isLeftPar(thisOne)) {
                    charStack.push(thisOne);
                } else if (isRightPar(thisOne)) {//Here
                    while (!isLeftPar(thisOne)) {
                        thisOne = charStack.pop();
                        if (!isLeftPar(thisOne)) {
                            rpnEXP = rpnEXP + thisOne + " ";
                        }
                    }
                } else {
                    if (charStack.isEmpty() || !lowerPriority(thisOne, charStack.peek())) {
                        charStack.push(thisOne);
                    } else {
                        do {
                            rpnEXP = rpnEXP + charStack.pop() + " ";
                        }
                        while (!charStack.isEmpty() && lowerPriority(thisOne, charStack.peek()));
                        charStack.push(thisOne);
                    }
                }
            }
        }
        while(!charStack.isEmpty()){
            rpnEXP = rpnEXP + charStack.pop() + " ";
        }
        return rpnEXP;
    }

    private static boolean isLeftPar(String aChar) {
        return aChar.equals("(");
    }

    /**
     * 返回新來的優先級是否是不大於棧頂.aChar > peek 就返回false,peek >= aChar 就返回true
     * @param aChar
     * @param peek
     * @return 返回true就彈棧,返回false就將新來的壓棧
     */
    private static boolean lowerPriority(String aChar, String peek) {
        if(peek.equals("(")){
            return false;
        }
        else if (aChar.equals("+") || aChar.equals("-")){
            return true;
        }else {
            if (peek.equals("*") || peek.equals("/")){
              return true;
            }else {
                return false;
            }
        }
    }

    private static boolean isRightPar(String c) {
        return c.equals(")");
    }

    private static boolean isNum(String c){
        Pattern pattern = Pattern.compile("[\\d]*$");
        return pattern.matcher(c).matches();
    }

    private static  boolean isSymbol(String c){
        String pattern = "[-/*+()]";
        return Pattern.matches(pattern,c) ;
    }
}

 


 

相關文章
相關標籤/搜索