大話數據結構系列之棧的實際應用(十)

斐波那契函數推導( Java、C )

圖解定義

C 語言實現

#include "stdio.h"

int Fbi(int i)  /* 斐波那契的遞歸函數 */
{
    if( i < 2 )
        return i == 0 ? 0 : 1;  
    return Fbi(i - 1) + Fbi(i - 2);  /* 這裏Fbi就是函數自己,等於在調用自己 */
}  

int main()
{
    int i;
    int a[40];  
    printf("迭代顯示斐波那契數列:\n");
    a[0]=0;
    a[1]=1;
    printf("%d ",a[0]);  
    printf("%d ",a[1]);  
    for(i = 2;i < 40;i++)  
    { 
        a[i] = a[i-1] + a[i-2];  
        printf("%d ",a[i]);  
    } 
    printf("\n");
    
    printf("遞歸顯示斐波那契數列:\n");
    for(i = 0;i < 40;i++)  
        printf("%d ", Fbi(i));  
    return 0;
}

Java 實現

package com.example.stack;

public class FibonacciTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("使用迭代法實現:");
        circulation();
        System.out.println("使用遞歸法實現:================================================");
        for(int i = 0;i < 40; i++){
            System.out.println("i="+i+",結果爲:"+fbi(i));
        }
    }
    
    public static void circulation() {
        int i;
        int a[] = new int[40];
        a[0] = 0;
        a[1] = 1;
        for(i = 2; i < 40; i++){
            a[i] = a[i-1] + a[i-2];
            System.out.println("i="+i+",結果爲:"+a[i]);
        }
    }
    
    public static int fbi(int i){
        if(i < 2)
            return i ==0 ? 0 : 1;
        return fbi(i - 1) + fbi(i - 2);
    }
}

使用棧來實現四則運算( Java、C )

後綴到輸出
1、從左到右遍歷表達式的每個數字和符號,遇到是數字就進棧
2、遇到符號,就將處於棧頂兩個數字出棧,進行運算,運算結果進棧,一直到最終獲得結果

中綴轉後綴
優先級:乘除優先加減
1、從左到右遍歷每個數字和符號,若是數字就輸出
2、若是符號,則判斷其餘棧頂符號的優先級,是右括號或優先級低於棧頂符號,則棧頂元素依次出棧並輸出,並將當前符號進棧,一直到最終輸出後綴表達式爲止

參考:–https://blog.csdn.net/weixin_44187963/article/details/90140882
Java 語言

package com.example.stack;
import java.util.Stack;
/*利用棧,進行四則運算的類
    後綴到輸出:
    **規則**:  
    1、從左到右遍歷表達式的每個數字和符號,遇到是數字就進棧
    2、遇到符號,就將處於棧頂兩個數字出棧,進行運算,運算結果進棧,一直到最終獲得結果
    實例:9 3 1 - 3 * + 10 2 / +    =>      20
    
    中綴轉後綴:
    **規則**:
    優先級:乘除優先加減
    1、從左到右遍歷每個數字和符號,若是數字就輸出
    2、若是符號,則判斷其餘棧頂符號的優先級,是右括號或優先級低於棧頂符號,則棧頂元素依次出棧並輸出,並將當前符號進棧,一直到最終輸出後綴表達式爲止
    實例: 9+(3-1)*3+10/2   =>     9 3 1 - 3 * + 10 2 / + 
 */
public class Operate {
    
    /*
     * 驗證數字串
     */
    public static void main(String args[]) {
        Operate operate = new Operate();
        //運算時加上
      /*  int t = operate.caculate("9+2*(3-1)+5*4/5"); 
        System.out.println(t);*/
        int t2 = operate.caculate("9+(3-1)*3+10/2");
        System.out.println(t2);
    }
    
    // 操作符棧
    private Stack<Character> priStack = new Stack<Character>();
    
    // 操作數棧
    private Stack<Integer> numStack = new Stack<Integer>();

    /**
     * @param str 需要進行技術的表達式
     * @return 計算結果
     */
    public int caculate(String str) {
        // 1.判斷string當中有沒有非法字符
        String temp;
        // 2.循環開始解析字符串,當字符串解析完,且符號棧爲空時,則計算完成
        StringBuffer tempNum = new StringBuffer();
        StringBuffer string = new StringBuffer().append(str).append("#");

        while (string.length() != 0) {
            temp = string.substring(0, 1);
            string.delete(0, 1);
            // 判斷temp,當temp爲操作符時
            if (!isNum(temp)) {
                // 1.此時的tempNum內即爲需要操作的數,取出數,壓棧,並且清空tempNum
                if (!"".equals(tempNum.toString())) {
                    // 當表達式的第一個符號爲括號
                    int num = Integer.parseInt(tempNum.toString());
                    numStack.push(num);
                    tempNum.delete(0, tempNum.length());
                }
                while (!compare(temp.charAt(0)) && (!priStack.empty())) {
                    // 第二個運算數
                    int a = (int) numStack.pop();
                    // 第一個運算數
                    int b = (int) numStack.pop();
                    char ope = priStack.pop();
                    int result = 0;
                    switch (ope) {
                        case '+':
                            result = b + a;
                            numStack.push(result);
                            break;
                        case '-':
                            result = b - a;
                            numStack.push(result);
                            break;
                        case '*':
                            result = b * a;
                            numStack.push(result);
                            break;
                        case '/':
                            result = b / a;
                            numStack.push(result);
                            break;
                    }

                }
                // 判斷當前運算符與棧頂元素優先級, 如果高,或者低於平,計算完後,將當前操作符號,放入操作符棧
                    priStack.push(new Character(temp.charAt(0)));
                    // 當棧頂爲'(',而當前元素爲')'時,則是括號內以算完,去掉括號
                    if (temp.charAt(0) == ')') {
                        priStack.pop();
                        priStack.pop();
                    }
            } else
                // 當爲非操作符時(數字)
                tempNum = tempNum.append(temp);
        }
        return numStack.pop();
    }

    /**
     * 判斷傳入的字符是不是0-9的數字
     *傳入的字符串
     * @return
     */
    private boolean isNum(String temp) {
        return temp.matches("[0-9]");
    }

    /**
     * @param str 需要進行比較的字符
     * @return 比較結果 true代表比棧頂元素優先級高,false代表比棧頂元素優先級低
     */
    private boolean compare(char str) {
        if (priStack.empty()) {
            // 當爲空時,顯然 當前優先級最低,返回高
            return true;
        }
        char last = (char) priStack.lastElement();
        // 如果棧頂爲'('顯然,優先級最低,')'不可能爲棧頂。
        if (last == '(') {
            return true;
        }
        switch (str) {
            case '#':
                return false;// 結束符
          case '(':
                // '('優先級最高,顯然返回true
                return true;
            case ')':
                // ')'優先級最低,
                return false;
            case '*': {
                // '*/'優先級只比'+-'高
                if (last == '+' || last == '-')
                    return true;
                else
                    return false;
            }
            case '/': {
                if (last == '+' || last == '-')
                    return true;
                else
                    return false;
            }
            // '+-'爲最低,一直返回false
            case '+':
                return false;
            case '-':
                return false;
        }
        return true;
    }
}

棧與遞歸的關係

遞歸
把一個直接調用自己或通過一系列的調用語句間接地調用自己的函數,稱做遞歸函數。

特點:每個遞歸定義必須至少有一個條件,滿足時遞歸不再進行,即不再引用自身而是返回值退出

以 fbi(5) 爲例,圖解遞歸展示:
在這裏插入圖片描述

關係
遞歸過程退回的順序是它前行順序的逆序
在退回過程中,可能要執行某些動作,包括恢復在前行過程中存儲起來的某些數據
這種存儲某些數據,並在後面又以存儲的逆序恢復這些數據,以提供之後使用的需求,顯然很符合棧這樣的數據結構。
更詳細解析:參見博主另一篇博客 <棧 與 遞歸 不得不說的“故事”>-- https://blog.csdn.net/weixin_39966065/article/details/104037329

棧與遞歸的實際功用
瀏覽器的撤回功能
不管什麼瀏覽器都有一個“後退”鍵,你點擊後可以按訪問順序的逆序加載瀏覽過的網頁。即使你從第一個網頁開始,連續點了幾十個鏈接跳轉,你點“後退”時,還是可以像歷史倒退一樣,回到之前瀏覽過的某個頁面

“遞歸”與“迭代”的選擇性討論

迭代:使用的是循環結構
利用變量的原值推出新值稱爲迭代,或着說迭代是函數內某段代碼實現循環;
例如:for,while循環

遞歸:使用的是選擇結構
重複調用函數自身實現循環稱爲遞歸;
例如:if else 調用自己,並在合適時機退出

對比圖解:

兩者關係:所有的迭代可以轉換爲遞歸,但遞歸不一定可以轉換成迭代。
圖:
在這裏插入圖片描述

詩詞即興環節

摘自”坐者“—— 程傑

  人生,就像是一個很大的棧演變。出生時你赤條條地來到人世,慢慢地長大,漸漸地變老,最終還是得赤條條地離開世間。
 人生,又彷彿是一天一天小小的棧重現。童年父母每天抱你不斷地進出家門,壯年你每天奔波於家與事業之間,老年你每天獨自蹣跚於養老院的門裏屋前。
 人生,更需要又進棧出棧精神的體現。在哪裏跌倒,就應該在哪裏爬起來。無論陷入何等困境,只要擡頭能仰望藍天,就有希望,不斷進取,你就可以讓出頭之日重現。困難不會永遠存在,強者才能勇往直前。
 人生,其實就是一個大大的隊列演變。無知童年、快樂少年,稚傲青年,成熟中年,安逸萬年。
 人生,又是一個又一個小小的隊列重現。穿下秋冬輪迴年年,早中晚夜循環天天。變化的是時間,不變的是你對未來執着的信念。
 人生,更需要又隊列精神的體現。南極到別急,不過是南緯 90 度到北緯 90 度的隊列,如果你中途猶豫,臨時轉向,也許你就只能和企鵝相伴永遠。可事實上,無論哪個方向,只要你堅持到底,你都可以到達終點。
 人生,或許如隊列,如棧,但它就是現在,這就是人生(樓主亂入:》)

發佈了240 篇原創文章 · 獲贊 91 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章