数据结构4
栈
栈的特点
1.栈的英文为(stack)
2.栈是一个先入后出(FILO-First In Last Out)的有序列表。
3.栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。
4.根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除
出栈和入栈
栈的应用
1.子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
2.处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
3.表达式的转换[中缀表达式转后缀表达式]与求值(实际解决)。
4.二叉树的遍历。
5.图形的深度优先(depth一first)搜索法。
数组模拟栈
模拟数组类
class ArrayStack {
private int maxSize;
//栈顶
private int top=-1;
private int[]stack;
//构造器
public ArrayStack(int size) {
super();
this.maxSize = size;
stack=new int [maxSize];
}
public boolean isFull() {
return top == maxSize - 1;
}
public boolean isEmpty() {
return top == -1;
}
//入栈
public void push(int value) {
if(isFull()) {
System.out.println("栈满");
return;
}
top++;
stack[top] = value;
}
//出栈
public int pop() {
if(isEmpty()) {
throw new RuntimeException("栈空,没有数据~");
}
int value = stack[top];
top--;
return value;
}
public void list() {
if(isEmpty()) {
System.out.println("栈空,没有数据~~");
return;
}
for(int i = top; i >= 0 ; i--) {
System.out.printf("stack[%d]=%d\n", i, stack[i]);
}
}
}
栈操作类
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(4);
String key = "";
boolean loop = true;
Scanner scanner = new Scanner(System.in);
while(loop) {
System.out.println("show: 输出栈全部内容");
System.out.println("exit: 退出程序");
System.out.println("push: 入栈");
System.out.println("pop: 出栈");
System.out.println("请输入:");
key = scanner.next();
switch (key) {
case "show":
stack.list();
break;
case "push":
System.out.println("请输入一个数");
int value = scanner.nextInt();
stack.push(value);
break;
case "pop":
try {
int res = stack.pop();
System.out.printf("出栈的数据是 %d\n", res);
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case "exit":
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出~~~");
}
栈应用实例—综合计算器
栈类
class ArrayStack2 {
private int maxSize;
private int[] stack;
private int top = -1;
public ArrayStack2(int maxSize) {
this.maxSize = maxSize;
stack = new int[maxSize];
}
//返回最后入栈的数
public int peek() {
return stack[top];
}
public boolean isFull() {
return top == maxSize - 1;
}
public boolean isEmpty() {
return top == -1;
}
public void push(int value) {
//先判断栈是否满
if(isFull()) {
System.out.println("栈满");
return;
}
top++;
stack[top] = value;
}
public int pop() {
//先判断栈是否空
if(isEmpty()) {
//抛出异常
throw new RuntimeException("栈空,没有数据");
}
int value = stack[top];
top--;
return value;
}
//显示栈的情况[遍历栈], 遍历时,需要从栈顶开始显示数据
public void list() {
if(isEmpty()) {
System.out.println("栈空,没有数据");
return;
}
//需要从栈顶开始显示数据
for(int i = top; i >= 0 ; i--) {
System.out.println("stack["+i+"]="+stack[i]);
}
}
//自定义优先级
public int priority(int oper) {
if(oper == '*' || oper == '/'){
return 1;
} else if (oper == '+' || oper == '-') {
return 0;
} else {
return -1;
}
}
//判断是不是一个运算符
public boolean isOper(char val) {
return val == '+' || val == '-' || val == '*' || val == '/';
}
//计算方法
public int cal(int num1, int num2, int oper) {
int res = 0;
switch (oper) {
case '+':
res = num1 + num2;
break;
case '-':
res = num2 - num1;
break;
case '*':
res = num1 * num2;
break;
case '/':
res = num2 / num1;
break;
default:
break;
}
return res;
}
}
栈实例类
public static void main(String[] args) {
String expression = "7*2*2-5+1-5+3-4*3";
ArrayStack2 num_list=new ArrayStack2(10);
ArrayStack2 oper_list=new ArrayStack2(10);
String keepnum="";
//用于存放取出的第一个数
int num1;
//用于存放取出的第二个数
int num2;
// 用于存放结果
int value;
// 用于存放操作符
int oper;
.........
}
实现对字符串的入栈出栈运算操作
//思路:1.循环扫描字符串
//2.若为数字就继续扫描直到下一个符号是运算符
//3.若为运算符并且运算符栈里面没有符号就将该运算符入栈
//4.若运算符栈里面有符号就判断是否优先级小于等于栈中符号,若是,则取出数字栈里面的两个数开始运算
//并将结果写入数字栈,由此可以让数字栈里面的数全为+-运算
//5.若运算优先级大于栈中符号就写入符号栈中
for (int index = 0; index < expression.length(); index++) {
char char1=expression.substring(index, index+1).charAt(0);
if (oper_list.isOper(char1)) {
if (oper_list.isEmpty()) {
oper_list.push(char1);
}else {
if (oper_list.priority(char1)<=oper_list.priority(oper_list.peek())) {
num1=num_list.pop();
num2=num_list.pop();
oper=oper_list.pop();
value=oper_list.cal(num1, num2, oper);
num_list.push(value);
System.out.println(value);
oper_list.push(char1);
}else {
oper_list.push(char1);
}
}
}else {
keepnum+=char1;
if (index==expression.length()-1) {
num_list.push(Integer.parseInt(keepnum));
}else {
if (oper_list.isOper(expression.substring(index+1,index+2).charAt(0))) {
num_list.push(Integer.parseInt(keepnum));
keepnum="";
}
}
}
}
最后对数字栈中的±运算结果算出
while(true) {
if(oper_list.isEmpty()) {
break;
}
num1 = num_list.pop();
num2 = num_list.pop();
oper = oper_list.pop();
value = num_list.cal(num1, num2, oper);
num_list.push(value);
}
//此处取出的一个数是最终的结果
int res2 = num_list.pop();
System.out.printf("表达式"+expression+"="+res2);
逆波兰表达式计算
中缀表达式转化成列表
public static List<String> toInfixExpressionList(String s) {
List<String >ls=new ArrayList<String>();
int i=0;
String str="";
char c;
do {
if ((c=s.charAt(i))<48 || (c=s.charAt(i))>57) {
ls.add(""+c);
i++;
}else {
str="";
while (i<s.length()&&(c=s.charAt(i))>48&&(c=s.charAt(i))<57) {
str+=c;
i++;
}
ls.add(str);
}
}while (i<s.length());
return ls;
}
String expre="1+((2+3)*4)-5";
将中缀列表转换成后缀列表
思路:
public static List<String> parseSuiffix(List<String> ls) {
//用于存放符号的栈
Stack<String>s1=new Stack<String>();
//用于存放数字的列表
List<String> list2=new ArrayList<String>();
for (String item :ls) {
//多位数\\d+
if (item.matches("\\d+")) {
list2.add(item);
}else if (item.equals("(")){
s1.push(item);
}else if (item.equals(")")){
while (!s1.peek().equals("(")){
list2.add(s1.pop());
}
s1.pop();
}else {
while (s1.size()!=0&&Operation.getValue(s1.peek())>=Operation.getValue(item)){
list2.add(s1.pop());
}
s1.push(item);
}
}
while (s1.size()!=0) {
list2.add(s1.pop());
}
return list2;
}
对于后缀列表的计算方式
public static int calculate(List<String>ls) {
Stack<String>stack=new Stack<String>();
int b1;
int b2;
for (String item:ls) {
if (item.matches("\\d+")) {
stack.push(item);
}else {
//依次pop出栈中的数据
//最后剩余的一个数即为结果
b2=Integer.parseInt(stack.pop());
b1=Integer.parseInt(stack.pop());
int res=0;
if (item.equals("+")) {
res=b1+b2;
}else if (item.equals("-")){
res=b1-b2;
}else if (item.equals("*")){
res=b1*b2;
}else if (item.equals("/")){
res=b1/b2;
}else {
throw new RuntimeException("运算符错误");
}
stack.push(res+"");
}
}
return Integer.parseInt(stack.pop());
}
操作符的优先级
class Operation{
private static int ADD=1;
private static int SUB=1;
private static int MUL=2;
private static int DIV=2;
public static int getValue(String oper) {
int result=0;
switch (oper) {
case "+":
result=ADD;
break;
case "-":
result=SUB;
break;
case "*":
result=MUL;
break;
case "/":
result=DIV;
break;
default:
break;
}
return result;
}
}
递归1
回顾递归调用机制
打印问题
public static void test(int n) {
if (n > 2) {
test(n - 1);
}
System.out.println("n=" + n);
}
阶乘问题
public static int factorial(int n) {
if (n == 1) {
return 1;
} else {
return factorial(n - 1) * n;
}}
递归实例—八皇后
八皇后问题介绍
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
代码演示
//放置第n+1个皇后
public void check(int n) {
//如果n=8既该放置第9个皇后, 说明第八个已经放好
if (n==8) {
//计数解法
count++;
return;
}
//回溯循环对于每一个放好的皇后都会找到把他放到下一个位置是否有正确解
for (int i = 0; i < 8; i++) {
array[n]=i;
//如果judge返回true就check下一个皇后
if (judge(n)) {
// System.out.println("array["+n+"]="+array[n]);
check(n+1);
}
}
}
//判断第n+1个皇后放的位置是否满足题意
private boolean judge(int n) {
judgecount++;
for (int i = 0; i < n; i++) {
if (array[n]==array[i]||Math.abs(n-i)==Math.abs(array[n]-array[i])) {
return false;
}
}
return true;
}