特殊的線性表-棧、佇列
阿新 • • 發佈:2018-12-23
在上一篇線性表我們會發現使用頭插法和尾插法會導致遍歷時的順序相反。棧和佇列就是這樣遵循一定插入和刪除規則的線性表。
棧:先入後出
佇列:先入先出
這裡我們用陣列(順序結構)來實現棧和佇列
棧的實現:
package Stack; //順序棧 public class Stack <T> extends Object{ private static final int DefaultSize=5; //棧的預設容量 public int Maxlength; //棧的容量 public int top; //棧頂指標(指向棧頂) publicObject[] element; public Stack() { element=new Object[DefaultSize]; top=-1; //當棧為空時,棧頂指標指向-1 Maxlength=DefaultSize; } //銷燬棧 public void destroyStack() { element=null; } //清空棧 public void clearStack() { top=0; }//判斷棧是否為空 public boolean isEmpty() { return top==0; } //判斷棧滿 public boolean isFull() { return top==Maxlength-1; } //遍歷棧 public void traversal() { int temp=top; while(temp!=-1) { System.out.println(element[temp]);--temp; } } //入棧 public void push(T x) { int temp=top; if(isFull()) { System.out.println("棧滿"); Object[] source=new Object[Maxlength]; while(temp!=-1) { source[temp]=element[temp]; --temp; } Maxlength=Maxlength*2; element=new Object[Maxlength]; System.out.println("棧容量大小擴充為:"+Maxlength); temp=top; while(temp!=-1) { element[temp]=source[temp]; --temp; } } element[++top]=new Object(); element[top]=x; } //出棧 public T pop() { if(top!=-1) { return (T)element[top--]; } System.out.println("棧空"); return null; } //返回棧內元素個數 public int stacklength() { return top+1; } }
測試:
package Stack; public class App { public static void main(String[] args) { // TODO Auto-generated method stub Stack<Integer> stack=new Stack<Integer>(); stack.push(1); stack.push(2); stack.push(3); stack.push(4); stack.push(5); stack.push(6); System.out.println("插入前遍歷"); stack.traversal(); System.out.println("棧容量大小:"+stack.Maxlength); stack.pop(); stack.push(7); System.out.println("插入後遍歷"); stack.traversal(); System.out.println("棧容量大小:"+stack.Maxlength); } }
結果:
棧滿 棧容量大小擴充為:10 插入前遍歷 6 5 4 3 2 1 棧容量大小:10 插入後遍歷 7 5 4 3 2 1 棧容量大小:10
兩個棧可以相互連線形成共享棧,共享棧空間,提高每個棧的利用率。
共享棧的實現:
package DoubleStack; //共享棧 public class DoubleStack<T> { private static final int DefaultSize=6; public int top1; public int top2; public int flag; //flag用來區分棧 public int Maxlength; public Object[] elements; public DoubleStack() { elements=new Object[DefaultSize]; Maxlength=DefaultSize; top1=-1; top2=Maxlength; } //判斷棧滿 public boolean isFull() { if(top1==top2-1) { return true; } return false; } //判斷棧空 public boolean isEmpty() { if(top1==-1&&top1==Maxlength) { return true; } return false; } //遍歷 public void traversal(int flag) { if(flag==1) { int temp=top1; while(temp!=-1) { System.out.println((T)elements[temp]); temp--; } return; } int temp=top2; while(temp!=Maxlength) { System.out.println((T)elements[temp]); temp++; } } //入棧 public void push(int flag,T x) { if(isFull()) { System.out.println("滿棧"); return; } if(flag==1) { elements[++top1]=new Object(); elements[top1]=x; return; } elements[--top2]=new Object(); elements[top2]=x; return; } //出棧 public T pop(int flag,T x) { if(isEmpty()) { System.out.println("空棧"); } if(flag==1) { if(top1!=-1) { return (T)elements[top1--]; } System.out.println("棧1為空"); return null; } if(flag==2) { if(top2!=Maxlength) { return (T)elements[top2++]; } System.out.println("棧2為空"); return null; } System.out.println("flag錯誤"); return null; } }
測試:
package DoubleStack; public class App { public static void main(String[] args) { // TODO Auto-generated method stub DoubleStack<Integer> doubleStack=new DoubleStack<Integer>(); doubleStack.push(1, 1); doubleStack.push(1, 2); doubleStack.push(1, 3); doubleStack.push(1, 4); // doubleStack.push(1, 5); // doubleStack.push(1, 6); // doubleStack.push(1, 7); doubleStack.push(2, 100); doubleStack.traversal(2); } }
結果:100
四則表示式求值-棧的應用
採用逆波蘭法
package FourExpression; import java.util.Stack; import java.util.regex.Pattern; //四則表示式計算,逆波蘭法的棧實現方式 public class StringToArithmetic { private StringToArithmetic() { } //方法:給出一個算術表示式(中綴表示式),得到計算結果。 例如 (5+8+10)*1,返回23 public static double StringtoArithmetic(String string) { return suffixToArithmetic(infixToSuffix(string)); } //將表示式轉換為字尾表示式 public static String infixToSuffix(String infix) { Stack<Character> stack=new Stack<Character>(); String suffix=""; int length=infix.length(); for(int i=0;i<length;i++) { Character temp; char c=infix.charAt(i); switch (c) { case ' ': break; case '(': stack.push(c); break; case '+': case '-': while (stack.size()!=0) { temp=stack.pop(); if(temp=='(') { stack.push('('); break; } suffix+=" "+temp; } stack.push(c); suffix+=" "; break; case '*': case '/': while(stack.size()!=0) { temp=stack.pop(); if(temp=='('||temp=='+'||temp=='-') { stack.push(temp); break; } else { suffix+=" "+temp; } } stack.push(c); suffix+=" "; break; case ')': while(stack.size()!=0) { temp=stack.pop(); if(temp=='(') { break; } else { suffix+=" "+temp; } } break; default: suffix+=" "+c; } } while (stack.size() != 0) { suffix += " " + stack.pop(); } return suffix; } //將字尾表示式計算出得到結果 public static double suffixToArithmetic(String postfix) { Pattern pattern = Pattern.compile("\\d+||(\\d+\\.\\d+)"); //使用正則表示式 匹配數字 String strings[] = postfix.split(" "); //將字串轉化為字串陣列 for (int i = 0; i < strings.length; i++) { strings[i].trim(); //去掉字串首尾的空格 } Stack<Double> stack = new Stack<Double>(); for (int i = 0; i < strings.length; i++) { if (strings[i].equals("")) continue; //如果是數字,則進棧 if ((pattern.matcher(strings[i])).matches()) { // System.out.println(Double.parseDouble(strings[i])); stack.push(Double.parseDouble(strings[i])); } else { //如果是運算子,彈出運算數,計算結果。 double y = stack.pop(); double x = stack.pop(); stack.push(caculate(x, y, strings[i])); //將運算結果重新壓入棧。 } } System.out.println("=="); return stack.pop(); //彈出棧頂元素就是運算最終結果。 } private static double caculate(double x, double y, String simble) { if (simble.trim().equals("+")) return x + y; if (simble.trim().equals("-")) return x - y; if (simble.trim().equals("*")) return x * y; if (simble.trim().equals("/")) return x / y; return 0; } }
測試:
package FourExpression; public class App { //此方法存在問題,還需修改(不能測試兩位數) public static void main(String[] args) { // TODO Auto-generated method stub String str = "9+(3-1)*3+8/2"; //其後綴表示式為9 3 1 - 3 * + 8 2 / + System.out.println("表示式:"+str); //呼叫方法:中綴表示式轉成字尾表示式 System.out.println("轉化為字尾表示式:"+StringToArithmetic.infixToSuffix(str)); //呼叫方法:給出一個算術表示式(中綴表示式),得到計算結果 System.out.println("由字尾表示式計算出結果:"+StringToArithmetic.StringtoArithmetic(str)); } }
結果:
表示式:9+(3-1)*3+8/2 轉化為字尾表示式: 9 3 1 - 3 * + 8 2 / + 由字尾表示式計算出結果:19.0
佇列的實現:
(要移動資料)
package Queue; public class Queue<T> extends Object{ private Object[] data=null; private static final int DefaultSize=10; //佇列的預設容量 private int maxLength; private int front; //佇列頭,允許刪除 private int rear; //佇列尾,允許插入 public Queue() { data=new Object[DefaultSize]; front=rear=0; maxLength=5; } //判斷佇列是否為空 public boolean isEmpty() { return rear==front; } public boolean isFull() { return rear==maxLength; } //入隊 public boolean push(T x) { if(isFull()) { System.out.println("佇列已滿"); return false; } else { data[rear]=x; rear++; return true; } } //出隊 public T pop() { if(isEmpty()) { System.out.println("佇列為空"); return null; } else { T temp=(T) data[0]; data[0]=null; for(int i=1;i<rear;i++) //移動O(n)的元素 { data[i-1]=data[i]; } rear--; maxLength--; return temp; } } //遍歷 public void traversal() { for(int i=0;i<rear;i++) { System.out.println(data[i]); } } //返回棧內元素個數 public int queuelength() { return maxLength; } }
測試:
package Queue; public class App { public static void main(String[] args) { // TODO Auto-generated method stub Queue queue=new Queue<Integer>(); queue.push(1); queue.push(2); queue.push(3); queue.push(4); queue.push(5); queue.push(6); System.out.println("出隊之前容量:"+queue.queuelength()); queue.traversal(); queue.pop(); System.out.println("出隊之後容量:"+queue.queuelength()); queue.traversal(); } }
結果:
佇列已滿 出隊之前容量:5 1 2 3 4 5 出隊之後容量:4 2 3 4 5
可以設定front指向頭結點,可以動態的標記佇列,就不需要在出隊之後前移其他元素,但會導致“假溢位”的現象,即出隊後空出的位置未被填補,而rear仍在不斷入隊,少數元素就撐滿佇列。
可以用迴圈佇列解決這個問題,即將新插入的元素新增到出隊而空出的位置,rear也指向下一位置。