在上一篇线性表我们会发现使用头插法和尾插法会导致遍历时的顺序相反。栈和队列就是这样遵循一定插入和删除规则的线性表。
栈:先入后出
队列:先入先出
这里我们用数组(顺序结构)来实现栈和队列
栈的实现:
package Stack; //顺序栈 public class Stack <T> extends Object{ private static final int DefaultSize=5; //栈的默认容量 public int Maxlength; //栈的容量 public int top; //栈顶指针(指向栈顶) public Object[] 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也指向下一位置。