數據結構(二) 棧應用

一. 算數表達式計算
     思路: (1)計算機要運行後綴表達式, 後綴表達式不用()表示優先級, 採用操作符的出現順序表示
               (2)中綴表達式轉化爲後綴表達式
                   1)初始化運算符棧, 遍歷表達式字符串
                   2)若字符爲操作數, 直接送往後綴表達式
                   3)若字符爲'(', 則直接入操作符棧
                   4)若字符爲運算符:
                          棧空時, 直接入棧
                          非空時, 從棧彈出優先級比該字符高的操作符加入後綴表達式, 再將本身入棧
                   5)若爲')' , 彈出操作符到後綴表達式, 直到棧頂操作符爲'(', 並丟棄'('
                   6)讀取完畢,若戰中還有操作符,全部彈出到後綴表達式
               (3)計算後綴表達式
                   1)初始化操作數棧, 遍歷後綴表達式
                   2)若爲操作數,直接入棧
                   3)若爲操作符, 彈出兩個操作數做運算, 結果入棧

public class StackExample{
	
	private int getPriority(char c){
		if(c=='('){
			return 0;
		}else if(c=='+'||c=='-'){
			return 1;
		}else if(c=='*'||c=='/'||c=='%'){
			return 2;
		}else if(c=='^'){
			return 3;
		}else{
			return -1;
		}
	}
	
	private String toPostfix(String expression) throws Exception{
		char[] chs = expression.toCharArray();
		LinkedStack<Character> opStack = new LinkedStack<Character>();
		StringBuilder sb = new StringBuilder();
		for(int i=0;i<chs.length;i++){
			char c = chs[i];
			if(c=='('){
				opStack.push(c);
			}else if(c==')'){
				char cc = opStack.pop();
				while(cc!='('){
					sb.append(cc);
					cc = opStack.pop();
				}
			}else if(getPriority(c)!=-1){ //運算符
				if(opStack.length()!=0){
					Character cc = opStack.pop();
					while(cc!=null&&getPriority(cc)>=getPriority(c)){//棧中的操作符可能都比新來的優先級高,則循環內的pop最後一次返回null
						sb.append(cc);
						cc = opStack.pop();
					}
					if(cc!=null)			//必須判空,跳出while的條件是cc==null,null會被壓棧
						opStack.push(cc);	//最後一次取操作符的優先級比c低,要重新壓棧
				}
				opStack.push(c);
			}else{
				sb.append(c);			//操作數直接加入string
			}
		}
		while(opStack.length()!=0){		//遍歷完字符數組,若棧中還有操作符,加入後綴表達式
			sb.append(opStack.pop());
		}
		return sb.toString();
	}
	
	public int count(String postfixStr) throws Exception{
		LinkedStack<Integer> numStack = new LinkedStack<Integer>();
		int result = 0;
		for(char c:postfixStr.toCharArray()){
			if(this.getPriority(c)==-1){	//操作數入棧
				numStack.push(Integer.parseInt(c+""));
			}else{
				int num1 = numStack.pop();
				int num2 = numStack.pop();  //num2是num1的下一個數
				if(c=='+'){
					result = num2 + num1;
				}
				if(c=='-'){
					result = num2 - num1;
				}
				if(c=='*'){
					result = num2 * num1;
				}
				if(c=='/'){
					result = num2/num1;
				}
				if(c=='%'){
					result = num2 % num1;
				}
				if(c=='^'){
					result = num2 ^ num1;
				}
				numStack.push(result);
			}
		}
		return numStack.pop();
	}
	
	public static void main(String[] args) throws Exception {
		StackExample sc = new StackExample();
		String str = sc.toPostfix("2+3*(4-1)");
		System.out.println(sc.count(str));
	}
}

二. 左右分隔符匹配

 思路: 括號的匹配是遵循就近原則的, 最裏面的左括號要和最裏面的右括號匹配,棧有記憶功能,用站記錄左括號
           (1) 若是左括號"(,[,{" , 則入棧
           (2) 若是右括號"),],}" , 去棧中匹配
public class StackExample{
	private LinkedStack<Character> stack = new LinkedStack<Character>();
	
	public boolean matches(char c1,char c2){
		if((c1=='('&&c2==')')||(c1=='{'&&c2=='}')||(c1=='['&&c2==']'))
			return true;
		return false;
	}
	
	public boolean isLegal(String str) throws Exception{
		char[] chs = str.toCharArray();
		for(char c:chs){
			if((c=='(')||(c=='{')||(c=='[')){
				stack.push(c);
			}
			if((c==')')||(c=='}')||(c==']')){
				if(stack.length()==0)	//若棧中沒有"左括號"可以和右括號匹配,則匹配失敗
					return false;
				if (!matches(stack.pop(),c))
					return false;
			}
		}
		if(stack.length()!=0)  //若匹配完"右括號",棧中仍有"左括號",則匹配失敗
			return false;
		return true;
	}
}
三. 大數加減
 思路 : 1) 兩個加數從最高位開始入棧, 這樣取的時候從個位取
            2) pop的兩個數相加, 個位數壓入sum棧, 十位數當做進位參與下次相加
            3) 若短的加數棧空, 則長的加數加上進位入sum棧, 
            4) 最後當兩個棧都空, 如果還有進位, 則進位入棧
public class StackExample{
	
	private LinkedStack<Integer> sum = new LinkedStack<Integer>();
	private LinkedStack<Integer> num1 = new LinkedStack<Integer>();
	private LinkedStack<Integer> num2 = new LinkedStack<Integer>();
	private int b = 0;
	
	private LinkedStack<Integer> string2Stack(String str){  //將string轉化成整數棧
		char[] chs = str.toCharArray();
		LinkedStack<Integer> stack = new LinkedStack<Integer>();
		for(int i=0;i<chs.length;i++){
			stack.push(Integer.parseInt(chs[i]+""));
		}
		return stack;
	}
	
	private void sum(LinkedStack<Integer> longs,LinkedStack<Integer> shorts) throws Exception{
		int i = 0;
		while(longs.length()!=0){	//兩個棧pop()的數相加再加上進位,就是這個'位'的和
			if(shorts.length()!=0){
				i = shorts.pop() + longs.pop() + b;
			}else{
				i = longs.pop() + b;
			}
			sum.push(i%10);
			b = i/10;
		}
		
		if(b!=0)	//最後如果還有進位,入棧(111+899=1000的情況,沒有這個結果是000)
			sum.push(b);
		
		while(longs.length()!=0)	//取出sum和
			sum.push(longs.pop());
	}
	
	public String sum(String str1,String str2) throws Exception{
		num1 = this.string2Stack(str1);
		num2 = this.string2Stack(str2);
		if(num1.length()>=num2.length()){
			this.sum(num1,num2);
		}else{
			this.sum(num2,num1);
		}
		StringBuilder sb = new StringBuilder();
		while(sum.length()!=0){
			sb.append(sum.pop());
		}
		return sb.toString();
	}
	
    public static void main(String[] args) throws Exception {
    	StackExample se = new StackExample();
    	System.out.println(se.sum("111", "9"));;
	}
}
四. 漢諾塔
      思路: 1. 只有一個圓盤時, 直接有x移到z
                2. n個圓盤時, 把前面的n-1個圓盤移到中間軸y後, 直接一共第一個圓盤到z
                3. 而前面的n-1個圓盤由x移到y, 又和原問題同解, 所以遞歸
                4. 最後吧n-1個圓盤由y移到z
int c = 0;
	private void move(int n,char x,char z){
		System.out.println("第"+ ++c +"次:"+n+"號"+x+"-->"+z);
	}
	private void hanoi(int n,char x,char y,char z){
		if(n==1){
			move(1,x,z);
		}else{
			hanoi(n-1,x,z,y);
			move(n,x,z);
			hanoi(n-1,y,x,z);
		}
	}
	@Test
	public void tess(){
		hanoi(3, 'x', 'y', 'z');
	}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章