導讀
棧和隊列是有操作限制的線性表。
目錄
1、棧的概念、特點、存儲結構。
2、棧的java實現及運用。
概念
棧是一種只允許在一端進行插入或刪除的線性表。
1、棧的操作端通常被稱為棧頂,另一端被稱為棧底。
2、棧的插入操作稱為進棧(壓棧|push);棧刪除操作稱為出棧(彈棧|pop)。
特點
棧就像一個杯子,我們只能從杯口放和取,所以棧中的元素是“先進后出”的特點。
存儲結構
順序存儲的棧稱為順序棧;鏈式存儲的棧稱為鏈式棧。
java實現
我們可以圍繞棧的4個元素來實現棧:
2狀態:是否棧空;是否棧滿。
2操作:壓棧push;進棧pop。
順序棧的實現
順序棧示意圖:
1 package test; 2 /** 3 * 4 * @author Fzz 5 * @date 2018年1月1日 6 * @Description TODO: 7 * 順序棧(SqStack)一般用數組來實現,主要有四個元素:2狀態2操作。 8 * 2狀態:棧空?;棧滿? 9 * 2操作:壓棧push;彈棧pop。 10 * @param <T> 11 */ 12 public class SqStack<T> { 13 private T data[];//用數組表示棧元素 14 private int maxSize;//棧空間大小(常量) 15 private int top;//棧頂指針(指向棧頂元素) 16 17 @SuppressWarnings("unchecked") 18 public SqStack(int maxSize){ 19 this.maxSize = maxSize; 20 this.data = (T[]) new Object[maxSize];//泛型數組不能直接new創建,需要使用Object來創建(其實一開始也可以直接使用Object來代替泛型) 21 this.top = -1;//有的書中使用0,但這樣會占用一個內存 22 } 23 24 //判斷棧是否為空 25 public boolean isNull(){ 26 boolean flag = this.top<=-1?true:false; 27 return flag; 28 } 29 30 //判斷是否棧滿 31 public boolean isFull(){ 32 boolean flag = this.top==this.maxSize-1?true:false; 33 return flag; 34 } 35 36 //壓棧 37 public boolean push(T vaule){ 38 if(isFull()){ 39 //棧滿 40 return false; 41 }else{ 42 data[++top] = vaule;//棧頂指針加1並賦值 43 return true; 44 } 45 } 46 47 //彈棧 48 public T pop(){ 49 if(isNull()){ 50 //棧為空 51 return null; 52 }else{ 53 T value = data[top];//取出棧頂元素 54 --top;//棧頂指針-1 55 return value; 56 } 57 } 58 59 }
測試:
package test; import org.junit.Test; public class test { @Test public void fun(){ //初始化棧(char類型) SqStack<Character> stack = new SqStack<Character>(10); //2狀態 System.out.println("棧是否為空:"+stack.isNull()); System.out.println("棧是否已滿:"+stack.isFull()); //2操作 //依次壓棧 stack.push('a'); stack.push('b'); stack.push('c'); //依次彈棧 System.out.println("彈棧順序:"); System.out.println(stack.pop()); System.out.println(stack.pop()); System.out.println(stack.pop()); } }

鏈式棧的實現
鏈式棧示意圖:
1 package test; 2 3 /** 4 * @author Fzz 5 * @date 2018年1月1日 6 * @Description TODO: 7 * 鏈棧(LinkStack)用鏈表來實現,主要有四個元素:2狀態2操作。 8 * 2狀態:棧空?;棧滿(邏輯上永遠都不會棧滿,除非你的電腦沒內存了^_^)。 9 * 2操作:壓棧push;彈棧pop。 10 * @param <T> 11 */ 12 class LinkStack<T>{ 13 //棧頂節點 14 private LinkNode<T> top; 15 16 //初始化1 17 public LinkStack(){ 18 this.top = new LinkNode<T>(); 19 } 20 21 //初始化棧 22 public void initStack(){ 23 this.top.setData(null); 24 this.top.setNext(null); 25 } 26 //是否棧空 27 public boolean isNull(){ 28 boolean flag = top.getNext()==null?true:false; 29 return flag; 30 } 31 32 //壓棧 33 public void push(LinkNode<T> node){ 34 if(isNull()){ 35 //棧空,即第一次插入 36 top.setNext(node); 37 node.setNext(null);//該句可以省略(首次插入的元素為棧底元素) 38 }else{ 39 node.setNext(top.getNext()); 40 top.setNext(node); 41 } 42 } 43 44 //彈棧 45 public LinkNode<T> pop(){ 46 if(isNull()){ 47 //棧空無法彈棧 48 return null; 49 }else{ 50 LinkNode<T> delNode = top.getNext();//取出刪除節點 51 top.setNext(top.getNext().getNext());//刪除節點 52 return delNode; 53 } 54 } 55 } 56 57 58 //鏈式棧節點(外部類實現,也可以使用內部類) 59 class LinkNode<T>{ 60 private T data;//數據域 61 private LinkNode<T> next;//指針域 62 63 //初始化1 64 public LinkNode(){ 65 this.data = null; 66 this.next = null; 67 } 68 69 //初始化2 70 public LinkNode(T data) { 71 super(); 72 this.data = data; 73 this.next = null; 74 } 75 public T getData() { 76 return data; 77 } 78 public void setData(T data) { 79 this.data = data; 80 } 81 public LinkNode<T> getNext() { 82 return next; 83 } 84 public void setNext(LinkNode<T> next) { 85 this.next = next; 86 } 87 }
測試:
1 package test; 2 3 import org.junit.Test; 4 5 public class test { 6 7 @Test 8 public void fun(){ 9 LinkStack<Character> ls = new LinkStack<Character>(); 10 11 //1狀態 12 System.out.println("棧是否為空:"+ls.isNull()); 13 14 //2操作 15 //依次壓棧 16 ls.push(new LinkNode<Character>('a')); 17 ls.push(new LinkNode<Character>('b')); 18 ls.push(new LinkNode<Character>('c')); 19 20 //依次彈棧 21 System.out.println("彈棧順序:"); 22 System.out.println(ls.pop().getData()); 23 System.out.println(ls.pop().getData()); 24 System.out.println(ls.pop().getData()); 25 } 26 }

棧的應用
棧結構是很基本的一種數據結構,所以棧的應用也很常見,根據棧結構“先進后出”的特點,我們可以在很多場景中使用棧,下面我們就是使用上面我們已經實現的棧進行一些常見的應用:十進制轉N進制、行編輯器、校驗括號是否匹配、中綴表達式轉后綴表達式、表達式求值等。
十進制轉換為N進制
如將十進制123456轉換為16進制,我們需要用123456除以16后取余壓棧,然后用商繼續除以16取余壓棧,重復以上操作,直到商為0,這樣保存在棧中的余數從棧頂到棧底開始排列形成的數字就是16進制了。(注:大於等於10的余數我們要用字母來表示)。
1 package test; 2 3 public class StackUtils{ 4 public static SqStack<?> ss ; 5 6 //彈棧出所有元素 7 public static Object[] popAll(SqStack<?> s){ 8 ss = s; 9 if(ss.isNull()){ 10 return null; 11 }else{ 12 Object[] array = new Object[ss.getTop()+1]; 13 int i = 0; 14 while(!ss.isNull()){ 15 array[i]=ss.pop(); 16 i++; 17 } 18 return array; 19 } 20 } 21 22 //使用棧進行進制裝換 23 public static String integerToNhex(Integer num,int hex){ 24 //對傳入的進制進行判斷 25 if(hex<=0||hex>36){ 26 return "請輸入有效的進制"; 27 }else if(num==0){ 28 return "0"; 29 }else if(num>0){//正數 30 SqStack<Integer> stack = new SqStack<Integer>(16); 31 int index = num; 32 while(num!=0){ 33 num = num / hex ; 34 int remainder = index % hex;//取余壓棧 35 stack.push(remainder); 36 index = num; 37 } 38 Object[] o = popAll(stack);//彈棧取出余數 39 StringBuilder sb = new StringBuilder(); 40 for(Object i : o){ 41 int in = (int)i; 42 //取出的數字如果>=10需要用字母代替 43 if(in>=10){ 44 char c = (char) ('a'+in-10); 45 sb.append(c); 46 }else{ 47 sb.append(i); 48 } 49 } 50 return sb.toString(); 51 }else{//負數 52 num = -num;//先去負號 53 SqStack<Integer> stack = new SqStack<Integer>(16); 54 int index = num; 55 while(num!=0){ 56 num = num / hex ; 57 int remainder = index % hex;//取余壓棧 58 stack.push(remainder); 59 index = num; 60 } 61 Object[] o = popAll(stack);//彈棧取出余數 62 StringBuilder sb = new StringBuilder(); 63 sb.append("-");//添加負號 64 for(Object i : o){ 65 int in = (int)i; 66 //取出的數字如果>=10需要用字母代替 67 if(in>=10){ 68 char c = (char) ('a'+in-10); 69 sb.append(c); 70 }else{ 71 sb.append(i); 72 } 73 } 74 return sb.toString(); 75 } 76 } 77 78 }
測試:
package test; import org.junit.Test; public class test { @Test public void fun(){ String s = StackUtils.integerToNhex(123456, 16); System.out.println("轉換得到的16進制數為:"+s); } }

校驗括號是否匹配
1 package test; 2 3 public class StackUtils{ 4 5 //表達式括號是否匹配()[]{} 6 public static boolean isMatch(String str) { 7 SqStack<Character> stack = new SqStack<Character>(str.length()+1); 8 char[] arr = str.toCharArray(); 9 for (char c : arr) { 10 //遇到左括號進棧 11 if(c=='('||c=='['||c=='{'){ 12 stack.push(c); 13 } 14 //遇到右括號匹配棧頂符號 15 else if(c==')'){ 16 if(stack.isNull()){ 17 return false;//棧為空,匹配失敗 18 }else if(stack.pop()=='('){ 19 continue;//匹配成功繼續下一次循環 20 }else{ 21 return false;//匹配不成功代表該表達式不符合規則 22 } 23 } 24 else if(c==']'){ 25 if(stack.isNull()){ 26 return false;//棧為空,匹配失敗 27 }else if(stack.pop()=='['){ 28 continue;//匹配成功繼續下一次循環 29 }else{ 30 return false;//匹配不成功代表該表達式不符合規則 31 } 32 } 33 else if(c=='}'){ 34 if(stack.isNull()){ 35 return false;//棧為空,匹配失敗 36 }else if(stack.pop()=='{'){ 37 continue;//匹配成功繼續下一次循環 38 }else{ 39 return false;//匹配不成功代表該表達式不符合規則 40 } 41 } 42 } 43 //如果最后沒有右括號但是還存在左括號表示不匹配 44 return stack.isNull(); 45 } 46 47 }
測試:
1 package test; 2 3 import org.junit.Test; 4 5 public class test { 6 @Test 7 public void fun(){ 8 String str1 = "i((l)o[v{e}]y)o{u}!";//表達式1:(()[{}]){} 9 String str2 = "you((do)[not{}])know{}!)";//表達式2:(()[{}]){}) 10 boolean match1 = StackUtils.isMatch(str1); 11 boolean match2 = StackUtils.isMatch(str2); 12 System.out.println("str1中的括號是否匹配:"+match1); 13 System.out.println("str2中的括號是否匹配:"+match2); 14 } 15 }

