尚硅谷算法与数据结构学习笔记04 -- 栈

Posted exodus3

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尚硅谷算法与数据结构学习笔记04 -- 栈相关的知识,希望对你有一定的参考价值。

1、栈的基本介绍

1.1、栈的实际需求

  • 请计算表达式:[7_2_2-5+1-5+3-3] 的值
  • 请问: 计算机底层是如何运算得到结果的? 注意不是简单的把算式列出运算,因为我们看这个算式 7 * 2 * 2 - 5,但是计算机怎么理解这个算式的
  • 对计算机而言, 它接收到的就是一个字符串, 我们讨论的是这个问题:

1.2、栈的基本性质

  • 栈的英文为(stack)
  • 栈是一个先入后出(FILO-First In Last Out)的有序列表
  • 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。 允许插入和删除的一端, 为变化的一端, 称为栈顶(Top), 另一端为固定的一端, 称为栈底(Bottom)。
  • 根据栈的定义可知, 最先放入栈中元素在栈底, 最后放入的元素在栈顶, 而删除元素刚好相反, 最后放入的元素最先删除, 最先放入的元素最后删除
  • 图解方式说明出栈(pop)和入栈(push)的概念

1.3、栈的应用场景

  • 子程序的调用: 在跳往子程序前, 会先将下个指令的地址存到堆栈中, 直到子程序执行完后再将地址取出, 以回到原来的程序中。
  • 处理递归调用: 和子程序的调用类似, 只是除了储存下一个指令的地址外, 也将参数、 区域变量等数据存入栈中。
  • 表达式的转换:[中缀表达式转后缀表达式]与求值(实际解决)。
  • 二叉树的遍历
  • 图形的深度优先(depth 一 first)搜索法

2、数组模拟栈

2.1、代码思路

  • maxSize :栈的大小(数组的大小)
  • arr :用来模拟栈的数组
  • top :指向当前栈顶元素,初始值为 -1 ,表示栈空
  • 判断栈满:top == maxSize ,即已经到达数组最后一个位置
  • 判断栈空:top == -1
  • 入栈:arr[++top] = arr;
  • 出栈:return arr[top–] ;

2.2、代码实现

  • 栈的定义
//定义一个 ArrayStack 表示栈
class ArrayStack 
	private int maxSize; // 栈的大小
	private int[] stack; // 数组,数组模拟栈,数据就放在该数组
	private int top = -1;// top表示栈顶,初始化为-1

	// 构造器
	public ArrayStack(int maxSize) 
		this.maxSize = maxSize;
		stack = new int[this.maxSize];
	

	// 栈满
	public boolean isFull() 
		return top == maxSize - 1;
	

	// 栈空
	public boolean isEmpty() 
		return top == -1;
	

	// 入栈-push
	public void push(int value) 
		// 先判断栈是否满
		if (isFull()) 
			System.out.println("栈满");
			return;
		
		top++;
		stack[top] = value;
	

	// 出栈-pop, 将栈顶的数据返回
	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 是否正确
    // 先创建一个ArrayStack对象->表示栈
    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();
        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("程序退出~~~");

  • 程序运行结果
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
push
请输入一个数
1
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
push
请输入一个数
2
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
push
请输入一个数
3
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
push
请输入一个数
4
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
push
请输入一个数
5
栈满
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
show
stack[3]=4
stack[2]=3
stack[1]=2
stack[0]=1
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
pop
出栈的数据是 4
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
pop
出栈的数据是 3
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
pop
出栈的数据是 2
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
pop
出栈的数据是 1
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择
pop
栈空,没有数据~
show: 表示显示栈
exit: 退出程序
push: 表示添加数据到栈(入栈)
pop: 表示从栈取出数据(出栈)

请输入你的选择

2.3、数组模拟栈全部代码

public class ArrayStackDemo 
    
 	public static void main(String[] args) 
 		// 测试一下ArrayStack 是否正确
 		// 先创建一个ArrayStack对象->表示栈
 		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();
 			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("程序退出~~~");
 	
 
 
 
 //定义一个 ArrayStack 表示栈
 class ArrayStack 
 	private int maxSize; // 栈的大小
 	private int[] stack; // 数组,数组模拟栈,数据就放在该数组
 	private int top = -1;// top表示栈顶,初始化为-1
 
 	// 构造器
 	public ArrayStack(int maxSize) 
 		this.maxSize = maxSize;
 		stack = new int[this.maxSize];
 	
 
 	// 栈满
 	public boolean isFull() 
 		return top == maxSize - 1;
 	
 
 	// 栈空
 	public boolean isEmpty() 
 		return top == -1;
 	
 
 	// 入栈-push
 	public void push(int value) 
 		// 先判断栈是否满
 		if (isFull()) 
 			System.out.println("栈满");
 			return;
 		
 		top++;
 		stack[top] = value;
 	
 
 	// 出栈-pop, 将栈顶的数据返回
 	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]);
 		
 	
 
 

2.4、课后练习

  • 使用链表模拟栈

3、栈实现综合计算器(中缀表达式)

3.1、代码思路

  • 栈分为两个栈:

    • 数栈(numStack):存储表达式中的数字
    • 符号栈(operStack):存储表达式中的符号
  • 扫描表达式(这里并没有考虑括号):

    • 对于数:扫描到数,则直接压入数栈
    • 对于运算符:扫描到运算符,分为如下几种情况:
      • 如果符号栈为空,则直接入栈
      • 如果符号栈不为空
        • 如果当前扫描到的运算符的优先级 <= 符号栈栈顶的运算符的优先级,说明上次的运算符优先级较高,先执行优先级高的运算
          • 从数栈中弹出两个数,根据符号栈栈顶的运算符进行运算(优先级高,就先算出来)
          • 然后将计算的结果压入栈中
          • 再将当前运算符压入符号栈
        • 如果当前扫描到的运算符的优先级 > 符号栈栈顶的运算符的优先级,说明上次的运算符优先级较低,直接压入符号栈
  • 何时停止循环?

    • 处理完表达式,退出循环
    • 即表达式下标(index)的值大于表达式(expression)的长度
    • 代码:index >= expression.length()
  • 表达式扫描完成:

    • 此时符号栈中的运算符优先级都相同
    • 从数栈中弹出两个数,再从符号栈中弹出一个运算符,进行运算,计算结果放回数栈中
    • 何时停止循环?符号栈为空则停止:operStack.isEmpty()
    • 表达式的值?符号栈为空时,数栈栈顶还有一个元素,这个元素的值就是表达式的值
  • 举例:3+2*6-2

    • 首先

      • 将 3 压入数栈
      • 将 + 压入符号栈
      • 将 2 压入数栈

      2
      3 +
      数栈 符号栈

    • 由于 * 优先级大于 + ,所以将 * 压入 符号栈,然后将 6 压入数栈

      6
      2 *
      3 +
      数栈 符号栈

    • 由于 - 优先级低于 * ,所以从数栈中弹出两个数(6 和 2),从符号栈中弹出一个运算符(*),进行运算,运算结果再压入数栈,然后将 - 压入符号栈

      12 -
      3 +
      数栈 符号栈

    • 将 2 压入数栈,表达式处理完毕

      2
      12 -
      3 +
      数栈 符号栈

    • 重复此过程,直至符号栈为空:从数栈中弹出两个数,再从符号栈中弹出一个运算符,进行运算,计算结果放回数栈中

      10
      3 +
      数栈 符号栈

      13
      数栈 符号栈

3.2、代码实现

  • 栈的定义:专为计算器而生的栈
  • 对于乘除法特别说明:由于栈先进后出的特点,num1 是运算符后面的数(减数、除数),num2 是运算符前的数(被减数、被除数),特别需要注意减法与除法的顺序
    • res = num2 - num1;
    • res = num2 / num1;
//先创建一个栈,直接使用前面创建好
//定义一个 CalcStack 表示栈, 需要扩展功能
class CalcStack 
	private int maxSize; // 栈的大小
	private int[] stack; // 数组,数组模拟栈,数据就放在该数组
	private int top = -1;// top表示栈顶,初始化为-1

	// 构造器
	public CalcStack(int maxSize) 
		this.maxSize = maxSize;
		stack = new int[this.maxSize];
	

	// 增加一个方法,可以返回当前栈顶的值, 但是不是真正的pop
	public int peek() 
		return stack[top];
	

	// 栈满
	public boolean isFull() 
		return top == maxSize - 1;
	

	// 栈空
	public boolean isEmpty() 
		return top == -1;
	

	// 入栈-push
	public void push(int value) 
		// 先判断栈是否满
		if (isFull()) 
			System.out.println("栈满");
			return;
		
		top++;
		stack[top] = value;
	

	// 出栈-pop, 将栈顶的数据返回
	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 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; // res 用于存放计算的结果
		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以上是关于尚硅谷算法与数据结构学习笔记04 -- 栈的主要内容,如果未能解决你的问题,请参考以下文章

尚硅谷算法与数据结构学习笔记06 -- 排序算法

尚硅谷算法与数据结构学习笔记07 -- 排序算法2

尚硅谷算法与数据结构学习笔记07 -- 排序算法2

尚硅谷算法与数据结构学习笔记02 -- 单链表

尚硅谷算法与数据结构学习笔记03 -- 双向链表

尚硅谷算法与数据结构学习笔记01 -- 稀疏数组和队列