2.1 栈
2.1.1站的特点及抽象数据类型
栈(Stack),也称堆栈,是一种操作受限的线性表,栈只允许在线性表的一端插入/删除等操作,不允许在其他位置插入/删除
在线性表中进行插入/删除的一端称为栈顶(top),栈顶保存的元素称为栈顶元素,相对的另一端称为栈低(bottom)
如果栈中没有元素称为空栈
向栈中插入元素,称为进栈或入栈,从栈中删除称为退栈或出栈
栈的插入/删除操作只允许在栈顶进行,后进栈必定先出栈,称为后进先出表(First lastOut 简称:Flos)
堆栈抽象数据类型的定义
ADT Stack{
数据对象:D={a0,a1..an, ai是同一种数据类型的元素 }
数据关系:R={<ai,ai+1>}
基本操作:{
getSize() 返回元素的个数
isEmpty() 判断堆栈是否为空
push(Object) 压栈,入栈
pop() 弹栈,出栈
peek() 返回栈顶元素
}ADTStack
}
2.1.2栈的顺序实现
通过数组存储堆栈的元素
堆栈的操作都在栈顶完成
1 package demo2; 2 /** 3 * 定义接口,定义栈的操作 4 * @author ASUS 5 * 6 */ 7 public interface MyStack { 8 int getSize(); //返回元素的个数 9 boolean isEmpty(); //判断堆栈是否为空 10 void push(Object e); //压栈,入栈 11 Object pop(); //弹栈,出栈 12 Object peek(); //返回栈顶元素 13 14 }
1 package demo2; 2 3 /** 4 * 堆栈的顺序实现 数组 5 * @author ASUS 6 * 7 */ 8 public class MyArrayStack implements MyStack { 9 private Object[]elements; //定义数组保存堆栈的元素 10 private static final int DEFAULT_CAPACITY=16; 11 private int top; //栈顶指针 12 //在无参构造中.对数组默认初始化 13 public MyArrayStack() { 14 elements=new Object[DEFAULT_CAPACITY]; 15 } 16 //在构造方法中,指定堆栈的初始化大小 17 private void MyArrayStack(int initialCapacity) { 18 elements=new Object[initialCapacity]; 19 } 20 //返回元素的个数 21 @Override 22 public int getSize() { 23 24 return top; 25 } 26 //判断堆栈是否为空 27 @Override 28 public boolean isEmpty() { 29 30 return top<=0; 31 } 32 //入栈/压栈 添加一个元素 33 @Override 34 public void push(Object e) { 35 //判断堆栈是否已满,如果堆栈已满,数组需要扩容 36 if(top>=elements.length){ 37 //定义新的数组,默认按两倍扩容 38 Object[] newDate=new Object[elements.length*2]; 39 40 //把原来数组的内容复制到数组中 41 for(int i=0;i<elements.length;i++){ 42 newDate[i]=elements[i]; 43 } 44 //让原来的数组名指向新的数组 45 elements=newDate; 46 } 47 //把元素存储到栈顶指针指向的位置 48 elements[top]=e; 49 //栈顶指针上移 50 top++; 51 } 52 53 //出栈, 54 @Override 55 public Object pop() { 56 //判断堆栈是否为空 57 if(top<=0){ 58 throw new StackOverflowError("栈以空"); 59 } 60 top--; 61 return elements[top]; 62 } 63 64 //返回栈顶元素,不删除 65 @Override 66 public Object peek() { 67 //判断堆栈是否为空 68 if(top<=0){ 69 throw new StackOverflowError("栈以空"); 70 } 71 return elements[top-1]; 72 } 73 //重写toString方法 74 @Override 75 public String toString() { 76 77 StringBuilder sb=new StringBuilder(); 78 sb.append("["); 79 //从栈顶到栈低 80 for(int i=top-1;i>=0;i--){ 81 sb.append(elements[i]); 82 if(i>0){ 83 sb.append(","); 84 } 85 86 } 87 // 88 sb.append("]"); 89 return sb.toString(); 90 } 91 92 }
1 package demo2; 2 3 public class MyStackTest { 4 5 public static void main(String[] args) { 6 //创建堆栈 7 MyArrayStack stack=new MyArrayStack(); 8 System.out.println(stack.isEmpty()); //true 9 System.out.println(stack.getSize()); //0 10 //压栈 11 stack.push("aa"); 12 stack.push("bb"); 13 stack.push("cc") ; 14 stack.push("dd"); 15 System.out.println(stack.isEmpty()); //false 16 System.out.println(stack.getSize()); //4 17 System.out.println(stack); 18 System.out.println("----------------"); 19 //弹栈顶 20 System.out.println(stack.peek()); 21 //出栈 22 System.out.println(stack.pop()); 23 System.out.println(stack); 24 } 25 26 }
运行截图:
2.1.3栈的链式实现
使用链表作为栈的存储结构,有时也称为链栈
栈只允许在线性表的一端进行操作,
1 package demo2; 2 3 import java.nio.Buffer; 4 5 public class MyLinkStack implements MyStack{ 6 private Node top; 7 private int size; 8 @Override 9 //返回堆栈个数 10 public int getSize() { 11 12 return size; 13 } 14 15 //是否为空 16 @Override 17 public boolean isEmpty() { 18 19 return size==0; 20 } 21 22 //压栈 23 @Override 24 public void push(Object e) { 25 //根据元素生成结点 26 Node pNode=new Node(e,top); 27 //修改top栈顶指向新的结点 28 top=pNode; 29 size++; 30 } 31 32 //出栈 33 @Override 34 public Object pop() { 35 //判断堆栈是否为空 36 if(size<1){ 37 throw new StackOverflowError("栈为空"); 38 } 39 Object oldDate=top.data; 40 top=top.next; 41 size--; 42 return oldDate; 43 } 44 45 //返回栈顶元素 46 @Override 47 public Object peek(){ 48 //判断堆栈是否为空 49 if(size<1){ 50 throw new StackOverflowError("栈为空"); 51 } 52 53 return top.data; 54 } 55 @Override 56 public String toString() { 57 StringBuilder sb=new StringBuilder(); 58 sb.append("["); 59 for(Node pNode=top;pNode!=null;pNode=pNode.next){ 60 61 62 sb.append(pNode.data); 63 if(pNode.next!=null){ 64 sb.append(","); 65 } 66 } 67 sb.append("]"); 68 69 return sb.toString(); 70 } 71 //定义一个内部类,描述链表的结点 72 private class Node{ 73 Object data; 74 Node next; 75 public Node(Object data, Node next) { 76 super(); 77 this.data = data; 78 this.next = next; 79 } 80 81 82 } 83 84 }
1 package demo2; 2 3 public class MyLinkStackTest { 4 5 public static void main(String[] args) { 6 MyLinkStack stack= new MyLinkStack(); 7 System.out.println(stack.isEmpty()); 8 System.out.println(stack.getSize()); 9 //压栈 10 stack.push("aaa"); 11 stack.push("bbb"); 12 stack.push("ccc"); 13 stack.push("ddd"); 14 System.out.println(stack); 15 System.out.println("-----------------"); 16 17 //出栈 18 System.out.println(stack.peek()); 19 System.out.println(stack); 20 } 21 22 }
2.1.3 简单案例
1.进制转化
package demo3; import demo2.MyArrayStack; /** * 使用栈完成进制转化 * @author ASUS * */ public class TestBaseConversion { public static void main(String[] args) { /** * 把一个十进制num转化为deciml指定的字符串 * num 接受十进制的字符串 * decimal 返回num这个整数对应的decimal进制的字符串 */ System.out.println(convert(100, 2)); } public static String convert(int num ,int decimal){ MyArrayStack stack=new MyArrayStack(); int remainder = num%decimal; //余数 while (num!=0){ stack.push(remainder);//余数 num=num/decimal; remainder=num%decimal; } //出栈,余数倒叙 StringBuilder sb=new StringBuilder(); while(!stack.isEmpty()){ sb.append(stack.pop()); } return sb.toString(); } }
2.监测表达式中括弧是否匹配
1 package demo3; 2 3 import demo2.MyArrayStack; 4 5 6 /** 7 * 监测表达式的括弧是否匹配 8 * @author ASUS 9 * 10 */ 11 public class TestBracketMatch { 12 13 public static void main(String[] args) { 14 //测试 15 System.out.println(bracketMatch("(({{}}")); 16 System.out.println(bracketMatch("(({{}}))")); 17 18 } 19 20 21 //检测expression表达式的括弧是否匹配 22 public static boolean bracketMatch(String expression){ 23 MyArrayStack stack = new MyArrayStack(); 24 //遍历整个表达式,如果是左括弧就入栈,如果是右括弧,就出栈判断是否匹配 25 for(int i=0;i<expression.length();i++){ 26 //取出表达式的每个字符 27 char cc=expression.charAt(i); 28 switch (cc) { 29 case '(': 30 case '[': 31 case '{': 32 stack.push(cc); //左括弧入栈,Java可以自动装箱与拆箱 33 break; 34 case '}': 35 if(!stack.isEmpty()&&stack.pop().equals('{')){ 36 break; 37 }else{ 38 return false; 39 } 40 case ']': 41 if(!stack.isEmpty()&&stack.pop().equals('[')){ 42 break; 43 }else{ 44 return false; 45 } 46 case ')': 47 if(!stack.isEmpty()&&stack.pop().equals('(')){ 48 break; 49 }else{ 50 return false; 51 } 52 53 } 54 55 } 56 //如果遍历完后,栈为空,表示括弧匹配 57 if(stack.isEmpty()){ 58 return true; 59 }else{ 60 return false; 61 } 62 } 63 }
3.算法表达式求值
思路:1.定义两个栈,一个存储操作符operator ,一个存储 operand
2.读取表达式,如果操作数就存到operand操作数 栈
(1).操作符栈为空,直接入栈
(2).吧操作符栈中的的栈顶操作与 当前操作操 作符进行比较
当前操作符优先级高,操作符入栈
当前操作符优先级低(栈顶操作优先级高,), 弹出栈顶操作符,从操作符中弹 出两个操作数进行运算,把运算 符存储到操作数栈,判断当前 操作符 =与栈顶操作符之间的关系
1 package demo3; 2 import demo2.MyArrayStack; 3 4 /** 5 * 使用栈来计算算出表达式的值 6 * @author ASUS 7 * 8 */ 9 public class TestCalculateExpression { 10 11 public static void main(String[] args) { 12 String expression="4+3+(6-10+2*3)*4"; 13 double result=calculate(expression); 14 System.out.println(result); 15 16 } 17 //定义方法计算指定次表达式的值 18 private static double calculate(String expression) { 19 MyArrayStack operatorStack=new MyArrayStack(); 20 MyArrayStack operandStack=new MyArrayStack(); 21 //遍历表达式的操作数与操作符 22 for(int i=0;i<expression.length();i++){ 23 char cc = expression.charAt(i); 24 //如果cc是数字 25 if(Character.isDigit(cc)){ 26 //取出操作数 27 StringBuilder sb=new StringBuilder(); 28 //只有是数字就是循环的一部分 29 while(Character.isDigit(cc)){ 30 sb.append(cc); 31 i++; 32 if(i>=expression.length()){ 33 break; 34 } 35 cc=expression.charAt(i); 36 } 37 //操作数入栈 38 operandStack.push(i); 39 //修正i变量的值 40 i--; 41 }else{ 42 //如果是操作符 43 //栈为空,直接把操作符入栈 44 if(operatorStack.isEmpty()){ 45 operatorStack.push(cc); 46 continue; 47 } 48 while(!operatorStack.isEmpty()){ 49 char op1=(char) operatorStack.peek(); 50 51 //操作符栈不为空的情况 52 if(compareOperator(op1,cc)<0){ 53 //当前运算符优先级高于栈顶运算符优先级 54 operatorStack.push(cc); 55 break; 56 }else if(compareOperator(op1,cc)==0){ 57 //当前运算符优先级等于栈顶运算符的优先级,只有一种情况,左边小括号遇到右边小括号 58 operatorStack.pop(); 59 break; 60 }else{ 61 //栈顶运算优先级高 62 //取出两个操作数 63 if(operandStack.isEmpty()){ 64 throw new RuntimeException( "表达式错误"); 65 } 66 double num1=Double.parseDouble(operandStack.pop().toString()); 67 if(operandStack.isEmpty()){ 68 throw new RuntimeException( "表达式错误"); 69 } 70 double num2=Double.parseDouble(operandStack.pop().toString()); 71 //取栈顶运算符 72 char operator=(char) operatorStack.pop(); 73 //计算num1和num2的值 74 double result=compute(operator,num2,num1); 75 //把结果存储到操作栈中 76 operandStack.push(result); 77 //如果栈为空,新的操作符入栈 78 if(operatorStack.isEmpty()){ 79 operatorStack.push(cc); 80 break; 81 } 82 } 83 84 } 85 } 86 } 87 //当表达式遍历完后,如果操作字符串不为空,需继续计算 88 while(!operatorStack.isEmpty()){ 89 if(operandStack.isEmpty()){ 90 throw new RuntimeException( "表达式错误"); 91 } 92 char operator=(char) operatorStack.pop(); 93 double num1=Double.parseDouble(operandStack.pop().toString()); 94 if(operandStack.isEmpty()){ 95 throw new RuntimeException( "表达式错误"); 96 } 97 double num2=Double.parseDouble(operandStack.pop().toString()); 98 if(operandStack.isEmpty()){ 99 throw new RuntimeException( "表达式错误"); 100 } 101 double result=compute(operator,num2,num1); 102 operandStack.push(result); 103 } 104 //当操作栈为空,操作数多余一个数表达式错误 105 if(operandStack.getSize()>1){ 106 throw new RuntimeException( "表达式错误"); 107 } 108 return Double.parseDouble(operandStack.pop().toString()); 109 } 110 private static double compute(char operator, double num1, double num2) { 111 switch (operator) { 112 case '+': 113 114 return num1+num2; 115 case '-': 116 117 return num1-num2; 118 case '*': 119 120 return num1*num2; 121 case '/': 122 123 return num1/num2; 124 125 } 126 return 0; 127 } 128 private static int compareOperator(char op1, char op2) { 129 if(op1=='+'||op1=='-'){ 130 if(op2=='*'||op2=='/'||op2=='('){ 131 return -1; 132 } 133 } 134 if(op1=='*'||op1=='/'){ 135 if(op2=='('){ 136 return -1; 137 } 138 } 139 if(op1=='('){ 140 if(op2==')'){ 141 return 0; 142 }else{ 143 return -1; 144 } 145 } 146 return 1; 147 } 148 149 }