程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 棧和隊列的應用,隊列應用

棧和隊列的應用,隊列應用

編輯:JAVA綜合教程

棧和隊列的應用,隊列應用


1.棧常見應用

1.1 括號匹配

問題描述:假設表達有兩種符號:圓的和方的,嵌套的順序任意,判斷嵌套是否正確,如 ([]()) 或 [[()]]均為正確,而 [(]) 或 (()] 均為不正確。

算法描述:

   (1)初始化一個空棧,順序讀入括號;

   (2)若是左括號直接進棧;

   (3)若是右括號,先出棧一個元素比較是否與其匹配,如匹配則繼續比較下一個括號,若不匹配則返回匹配失敗;

   (4)最後若棧不為空,說明還有沒匹配的括號,匹配失敗,否則匹配成功。

/**
 * 檢查括號是否匹配
 * @param match 待匹配字符串
 * @return true 匹配成功,false 匹配失敗
 */
public static boolean isMatching(String match){
    char[] braces = match.trim().toCharArray();
    for(char ch : braces){
        if(ch == '[' || ch == '('){
            stack.push(ch);
        } else if(ch == ']'){
            char t = stack.pop();
            if('[' == t){
                continue;
            } else {
                return false;
            }
        } else if(ch == ')'){
            char c = stack.pop();
            if('(' == c){
                continue;
            } else {
                return false;
            }
        }
    }
    if(stack.size() > 0){
        return false;
    } else {
        return true;
    }
}

1.2 表達式求值

      算術表達式由括號、運算符和操作數組成,表達式求值的關鍵在於如何解析字符串,並按照正確的順序完成運算。表達式有中綴表達式,不僅要考慮運算符優先級,還要考慮括號,後綴表達式已經考慮了優先級,只有操作數和運算符,在此提供兩種方案解決表達式求值。

      首先定義運算符優先級,運算符只存在兩個位置,棧內和棧外,優先級也分為棧內優先級(in stack priority)和棧外優先級(in comming priority),棧的特性是後進先出,棧外的運算符優先級大,則入棧它迫切先計算,對於相同級別的運算符,棧內的優先級大於棧外的優先級,則出棧。據此設計運算符優先級表:(用isp,icp分別表示棧內和棧外優先級,#號表示字符串結束)

表1 運算符優先級表

操作符

#

(

+,-

*,/

)

isp

0

1

3

5

6

icp

0

6

2

4

1

      在Java中使用兩個HashMap存儲棧內和棧外運算符優先級,代碼如下:

static ArrayStack<String> ops = new ArrayStack<String>(10); // 運算符棧
static ArrayStack<Double> vals = new ArrayStack<Double>(20); // 操作數棧

static String opstr = "(+-*/)"; // 涉及到的運算符

static Map<String, Integer> isp = new HashMap<String, Integer>(); // 棧內優先級     
static Map<String, Integer> icp = new HashMap<String, Integer>(); // 棧外優先級
static{
    isp.put("#", 0);isp.put("(", 1); isp.put("+", 3); isp.put("-", 3); isp.put("*", 5); isp.put("/", 5); isp.put(")", 6);
    icp.put("#", 0);icp.put("(", 6); icp.put("+", 2); icp.put("-", 2); icp.put("*", 4); icp.put("/", 4); icp.put(")", 1);
}

1.2.1 直接使用中綴表達式求值

   算法描述:

   (1)初始化兩個棧,操作數棧和運算符棧,順序讀取字符串

   (2)如果是操作數直接入操作數棧;

   (3)如果是運算符,如果當前運算符優先級大於運算符棧頂優先級,則直接入運算符棧,如果小於則循環彈出一個操作符,兩個操作數計算,並將結果壓入操作數 棧,直到找到運算符優先級比它小的,最後如果當前運算符是右括號,則彈出一個運算符(彈出的是左括號),否則入運算符棧;

   (4)如果字符為#說明已經到表達式末尾,則循環彈出操作符棧中的運算符及操作數計算,直到運算符棧為空,最後操作數棧中的元素即為結果。

/**
 * 中綴表達式計算
 */
public static double infix(String expr){
    ops.push("#");
    char[] chs = expr.trim().toCharArray();
    for(char ch : chs) {
        String op = String.valueOf(ch);
        // 如果是# 表示表達式結尾,把棧中的運算符全部彈出
        if("#".equals(op)){
            while(ops.size() > 0 && !"#".equals(ops.peek())){
                vals.push(calculate(vals.pop(), vals.pop(), ops.pop().charAt(0)));
            }
        } else if(!opstr.contains(op)){// 如果是操作數,直接輸出
            vals.push(Double.valueOf(op));
        } else { // 運算符
            // 當前運算符大於棧頂運算符優先級,則直接入操作符棧
            if(icp.get(op) > isp.get(ops.peek())){
                ops.push(op);
            } else {
                // 如果小於,循環出棧找到比它優先級小的,彈出一個操作符,兩個操作數,計算結果置入操作數棧
                // 如果是 右括號) 即是匹配棧內的左括號),最後彈出),(不入棧
                // 如果不是右括號,最後把這個運算符壓入運算符棧
                while(icp.get(op) < isp.get(ops.peek())){
                    vals.push(calculate(vals.pop(), vals.pop(), ops.pop().charAt(0))); 
                }
                
                if(!")".equals(op)){
                    ops.push(op);
                } else {
                    ops.pop(); //彈出  ( 左括號
                }
            }
        }
    }
    return vals.pop();
}

1.2.2 使用後綴表達式求值

      後綴表達式已經包含了操作數以及運算符優先級,計算比較方便,遇操作數直接入棧,遇運算符彈出兩個操作數計算後再入棧,如此循環,最後操作數棧內即為結果。關鍵在於如何把中綴表達式轉為後綴表達式。

   中綴表達式轉後綴表達式,算法描述:

   (1)初始化一個運算符棧,順序讀取表達式字符串

   (2)如果字符是操作數直接輸出

   (3)如果字符是運算符,如果當前運算符優先級大於運算符棧頂優先級,則直接入運算符棧,如果小於則循環彈出一個操作符,直到找到運算符優先級比它小的,最 後如果當前運算符是右括號,則彈出一個運算符(彈出的是左括號),否則入運算符棧;

   (4)若字符為# 表示已經到末尾,循環彈出運算符棧中的元素,直到棧空,最後的輸出即為中綴表達式。

/**
 * 中綴表達式轉後綴
 * @param expr
 */
public static String infix2suffix(String expr){
    ops.push("#");
    StringBuffer sb = new StringBuffer();
    char[] chs = expr.trim().toCharArray();
    for(char ch : chs){
        String op = String.valueOf(ch);
        
        // 如果是# 表示表達式結尾,把棧中的運算符全部彈出
        if("#".equals(op)){
            while(ops.size() > 0 && !"#".equals(ops.peek())){
                sb.append(ops.pop());
            }
        } else if(!opstr.contains(op)){// 如果是操作數,直接輸出
            sb.append(op);
        } else { // 運算符
            // 如果當前運算符優先級大於棧頂運算符優先級,則入棧
            if(icp.get(op) > isp.get(ops.peek())) {
                ops.push(op);
                continue;
            } 
            // 如果小於,彈出一個操作符,循環出棧直到比它優先級小
            // 如果是 右括號) 即是匹配棧內的左括號),最後彈出),(不入棧
            // 如果不是右括號,最後把這個運算符壓入運算符棧
            while(icp.get(op) < isp.get(ops.peek())){
                sb.append(ops.pop());
            }
            
            if(!")".equals(op)){
                ops.push(op);
            } else {
                ops.pop(); //彈出  ( 左括號
            }
        }
    }
    return sb.toString();
}

1.2.3 遞歸

    所謂遞歸,就是函數調用了自己,以斐波那契數列為例,其定義為:

這是一個典型的遞歸例子,Java程序實現如下:

public static int Fib(int n){
    if(n == 0){
        return 0;
    } else if(n == 1){
        return 1;
    } else {
        return Fib(n-1) + Fib(n-2);
    }
}

   遞歸的效率不高,由於其中重復包含很多重復的計算。每個線程執行時,都有各自的棧,我們在debug時可以看到,在遞歸調用時,遞歸期間的數據都是保存在這個棧中,系統幫我們維護這個棧。

   通常情況下將遞歸程序轉非遞歸程序,使用自定義棧模擬遞歸過程,幾乎適用於所有遞歸,站在系統的角度,棧能解決所有問題。但是具體問題,也有其他方法轉為非遞歸,如斐波那契數列可使用直接迭代計算出結果。

/**
 * 非遞歸實現,不借助棧,直接迭代
 * @return
 */
public static int NotRecur(int n){
    if(n == 0){
        return 0;
    } else if(n == 1){
        return 1;
    } else {
        int n0 = 0,
            n1 = 1; // n 為0,1時的初值
        while(n >= 2){
            int tmp = n1 + n0;
            n0 = n1;
            n1 = tmp;
            n--;
        }
        return n1;
    }
}

後續有些算法,如二叉樹的遍歷,會分為遞歸實現和非遞歸實現。

2.隊列常見應用

2.1 層次遍歷

在信息處理時,有一類問題需要逐層或逐行遍歷,比如二叉樹的層次遍歷,圖的廣度優先搜索等。

2.2 緩沖區

緩沖區,是為了匹配速度,主要解決主機和外設速度不匹配,解決多用戶引起資源競爭問題。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved