【韩顺平-数据结构】栈(学习笔记)

一、栈的概述

1、栈的介绍

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

2、栈的应用场景

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

3、栈的代码实现

1)数组实现

package work.rexhao.stack;

public class arrayStackdemo {

	public static void main(String[] args) {
		arrayStact as = new arrayStact(10);
		as.push(1);
		as.push(2);
		as.list();
		System.out.println(as.pop());
		System.out.println(as.pop());
		as.list();
	}

}

class arrayStact {
	int maxSize;
	int[] data;
	int top;

	/**
	 * 栈的构造器
	 * 
	 * @param maxSize 栈的大小
	 */
	arrayStact(int maxSize) {
		this.maxSize = maxSize;
		data = new int[maxSize];
		top = -1;
	}

	/**
	 * 判空
	 */
	public boolean isEmpty() {
		return top == -1;
	}

	/**
	 * 判满
	 */
	public boolean isFull() {
		return top == maxSize - 1;
	}

	/**
	 * 弹出
	 */
	public int pop() {
		if (isEmpty()) {
			System.out.println("栈空");
			return -1;
		}
		return data[top--];
	}

	/**
	 * 压栈
	 */
	public void push(int num) {
		if (isFull()) {
			System.out.println("栈满");
		} else {
			data[++top] = num;
		}
	}

	/**
	 * 遍历
	 */
	public void list() {
		if (isEmpty()) {
			System.out.println("栈空");
			return;
		}
		for (int i = 0; i <= top; i++) {
			System.out.printf("data[%d] = %d\n", i, data[i]);
		}
	}
}

2)链表实现

package work.rexhao.stack;

public class linkedListStackDemo {
	public static void main(String[] args) {
		linkedListStack lls = new linkedListStack();
		try {
			lls.pop();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		lls.push(1);
		lls.push(2);
		lls.pop();
		lls.push(3);
		lls.push(4);
		lls.show();
	}
}

class linkedListStack {
	private stackNode head = new stackNode(-1);

	/**
	 * 压栈
	 */
	public void push(int num) {
		if (head.next == null) {
			head.next = new stackNode(num);
			return;
		}
		stackNode temp = head.next;
		while (true) {
			if (temp.next == null) {
				temp.next = new stackNode(num);
				return;
			}
			temp = temp.next;
		}

	}

	/**
	 * 出栈
	 */
	public int pop() {
		if (isEmpty()) {
			throw new RuntimeException("栈空");
		}
		stackNode temp = head;
		while (true) {
			if (temp.next.next == null) {
				break;
			}
			temp = temp.next;
		}
		int ans = temp.next.getData();
		temp.next = null;
		return ans;
	}

	/**
	 * 判空
	 */
	public boolean isEmpty() {
		return head.next == null;
	}

	/**
	 * 遍历
	 */
	public void show() {
		if (isEmpty()) {
			return;
		}
		stackNode temp = head.next;
		while (true) {
			if (temp == null) {
				return;
			}
			System.out.println(temp.toString());
			temp = temp.next;
		}
	}
}

class stackNode {
	private int data;
	public stackNode next;

	stackNode(int data) {
		this.data = data;
	}

	public int getData() {
		return data;
	}

	public void setData(int data) {
		this.data = data;
	}

	@Override
	public String toString() {
		return data + "";
	}
}

二、栈实现综合计算器

1、思路

  1. 通过一个index 值(索引),来遍历我们的表达式
  2. 扫描到一个数字,直接入数栈
  3. 扫描到一个符号
    1. 符号栈为空,就直接入栈
    2. 有操作符,就进行比较
      1. 如果当前的操作符的优先级小于或者等于栈顶的操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果入数栈,然后将当前的操作符入符号栈
      2. 如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈
  4. 当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号
  5. 最后在数栈只有一个数字,就是表达式的结果

2、代码实现

package work.rexhao.stack;

import java.util.Scanner;

/**
 * 个位数四则计算器
 *
 */
public class calculatorDemo {
	public static void main(String[] args) {
		System.out.println("请输入表达式");
		Scanner sc = new Scanner(System.in);
		String s = sc.nextLine();
		sc.close();
		System.out.println("表达式值为:" + calculator.calculate(s));
	}

}

class calculator {

	/**
	 * 计算
	 * 
	 * @param s 表达式
	 * @return 结果
	 */
	public static int calculate(String s) {
		/*
		 * 初始化栈
		 */
		numStact ns = new numStact(20); // 数栈
		signStact ss = new signStact(20); // 运算符栈

		/*
		 * 扫描字符串
		 */
		int slen = s.length();
		// int index = 0; // 扫描索引
		for (int i = 0; i < slen; i++) {
			char c = s.charAt(i);
			if (c == '*' || c == '/') {
				ss.push(c);
			} else if (c == '+' || c == '-') {
				while (true) {
					// 栈空:入栈
					if (ss.isEmpty()) {
						break;
					}
					// 栈非空:比较
					if (ss.top() == '+' || ss.top() == '-') {
						// 同优先级
						break;
					} else {
						// 大于优先级
						// 1.取出两个数和运算符
						int ans = count(ns.pop(), ns.pop(), ss.pop());
						// 2.结果入栈
						ns.push(ans);
					}
				}
				ss.push(c);
			} else {
				ns.push(c - '0');
			}
		}
		while(ns.top != 0) {
			ns.push(count(ns.pop(), ns.pop(), ss.pop()));
		}
		return ns.pop();
	}

	/**
	 * 辅助计算
	 */
	private static int count(int a, int b, char c) {
		if (c == '+') {
			return a + b;
		} else if (c == '-') {
			return b - a;
		} else if (c == '*') {
			return a * b;
		} else if (c == '/') {
			return a / b;
		}
		throw new RuntimeException("NaN");
	}
}

/**
 * 数栈
 * 
 */
class numStact {
	int maxSize;
	int[] data;
	int top;

	/**
	 * 栈的构造器
	 * 
	 * @param maxSize 栈的大小
	 */
	numStact(int maxSize) {
		this.maxSize = maxSize;
		data = new int[maxSize];
		top = -1;
	}

	/**
	 * 判空
	 */
	public boolean isEmpty() {
		return top == -1;
	}

	/**
	 * 判满
	 */
	public boolean isFull() {
		return top == maxSize - 1;
	}

	/**
	 * 弹出
	 */
	public int pop() {
		if (isEmpty()) {
			System.out.println("栈空");
			return -1;
		}
		return data[top--];
	}

	/**
	 * 压栈
	 */
	public void push(int num) {
		if (isFull()) {
			System.out.println("栈满");
		} else {
			data[++top] = num;
		}
	}

	/**
	 * 遍历
	 */
	public void list() {
		if (isEmpty()) {
			System.out.println("栈空");
			return;
		}
		for (int i = 0; i <= top; i++) {
			System.out.printf("data[%d] = %d\n", i, data[i]);
		}
	}
}

/**
 * 符号栈
 *
 */
class signStact {
	int maxSize;
	char[] data;
	int top;

	/**
	 * 栈的构造器
	 * 
	 * @param maxSize 栈的大小
	 */
	signStact(int maxSize) {
		this.maxSize = maxSize;
		data = new char[maxSize];
		top = -1;
	}

	/**
	 * 判空
	 */
	public boolean isEmpty() {
		return top == -1;
	}

	/**
	 * 判满
	 */
	public boolean isFull() {
		return top == maxSize - 1;
	}

	/**
	 * 弹出
	 */
	public char pop() {
		if (isEmpty()) {
			throw new RuntimeException("栈空");
		}
		return data[top--];
	}

	/**
	 * 压栈
	 */
	public void push(char c) {
		if (isFull()) {
			System.out.println("栈满");
		} else {
			data[++top] = c;
		}
	}

	/**
	 * 遍历
	 */
	public void list() {
		if (isEmpty()) {
			System.out.println("栈空");
			return;
		}
		for (int i = 0; i <= top; i++) {
			System.out.printf("data[%d] = %s\n", i, data[i]);
		}
	}

	/**
	 * 栈顶元素
	 */
	public char top() {
		return data[top];
	}
}

三、逆波兰计算器

package work.rexhao.stack;

public class polandNotationDemo {

	public static void main(String[] args) {
		System.out.println(polandNotation.calculate("34+5*6-"));
	}

}

class polandNotation {
	public static int calculate(String s) {
		numStact ns = new numStact(20);
        // 不需要符号栈
		int slen = s.length();
		for (int i = 0; i < slen; i++) {
			char c = s.charAt(i);
			if (c == '*' || c == '/' || c == '+' || c == '-') {
				ns.push(count(ns.pop(), ns.pop(), c));
			} else {
				ns.push(c - '0');
			}
		}
		return ns.pop();
	}

	/**
	 * 辅助计算
	 */
	private static int count(int a, int b, char c) {
		if (c == '+') {
			return a + b;
		} else if (c == '-') {
			return b - a;
		} else if (c == '*') {
			return a * b;
		} else if (c == '/') {
			return a / b;
		}
		throw new RuntimeException("NaN");
	}
}

四、中缀转后缀

1、思路

后缀表达式适合计算式进行运算,但是却不太容易写出来,尤其是表达式很长的情况下

因此在开发中,我们常常需要将中缀表达式转成后缀表达式

  1. 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
  2. 从左至右扫描中缀表达式
  3. 遇到操作数时,将其压s2
  4. 遇到运算符时,比较其与 s1栈顶运算符的优先级
    1. 如果s1为空,或栈顶运算符为左括号(,压入s1
    2. 若该运算符优先级比栈顶运算符的高,压入s1
    3. 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到与与s1中新的栈顶运算符相比较
  5. 遇到括号
    1. 如果是左括号(,则直接压入s1
    2. 如果是右括号),则依次弹出s1的运算符压s2,直到遇到左括号为止,将这一对括号丢弃
  6. 重复步骤直到表达式的最右边
  7. 将s1中剩余的运算符依次弹出并压入s2
  8. 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

2、代码实现

/**
 * 中缀转后缀
 * 
 * @param s 中缀表达式
 * @return 后缀表达式
 */
public static String toPoland(String s) {
    sStact s1 = new sStact(20);// 运算符栈
    sStact s2 = new sStact(20);// 中间结果栈
    int slen = s.length();
    for (int i = 0; i < slen; i++) {
        char c = s.charAt(i);
        if (s1.isEmpty() || c == '(') {
            s1.push(c);
        } else if (c == ')') {
            while (true) {
                char temp = s1.pop();
                if (temp == '(') {
                    break;
                }
                s2.push(temp);
            }
        } else if (c == '+' || c == '-') {
            while (true) {
                if (s1.isEmpty() || s1.top() == '(') {
                    s1.push(c);
                    break;
                }
                s2.push(s1.pop());
            }
        } else if (c == '*' || c == '/') {
            while (true) {
                if (s1.isEmpty() || s1.top() == '+' || s1.top() == '-' || s1.top() == '(') {
                    s1.push(c);
                    break;
                }
                s2.push(s1.pop());
            }
        } else {
            // 数字
            s2.push(c);
        }
    }
    while (!s1.isEmpty()) {
        s2.push(s1.pop());
    }
    StringBuilder sb = new StringBuilder();
    while (!s2.isEmpty()) {
        sb.append(s2.pop());
    }
    return sb.reverse().toString();
}

更多文章欢迎访问RexHao博客:栈 | RexHao Blog

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
B站上的韩顺平老师的《Linux学习笔记》系列课程非常值得推荐。通过这个课程,我学到了很多关于Linux操作系统的知识和技能。 首先,韩老师在课程中详细介绍了Linux的基本概念和特点。我清楚地了解到Linux是一个开源的操作系统,具有稳定性、安全性和可定制性强的特点。这让我对Linux有了更深入的理解,也更有信心去学习和使用它。 其次,韩老师从基础开始,逐步讲解了Linux的安装和配置。他用简单明了的语言和实际操作的示范,帮助我了解了如何在虚拟机上安装Linux系统,并设置网络、用户账户、文件系统等。这为我后续的学习和实践打下了坚实的基础。 此外,韩老师还讲解了Linux的常用命令和工具。他详细介绍了常用的文件和目录操作命令,比如cd、ls、mkdir、cp等。同时,他还讲解了grep、sed、awk等强大的文本处理工具的使用方法。这些内容帮助我更加高效地进行文件管理和数据处理。 最后,韩老师还介绍了Linux的网络管理和安全防护。他讲解了如何配置网络连接、使用ssh远程登录以及设置防火墙等内容。这些知识对我了解网络和保护系统安全非常有帮助。 总的来说,韩顺平老师的《Linux学习笔记》课程非常实用,对于初学者来说是入门学习Linux的好选择。他通过深入浅出的讲解和丰富的实操示范,让我可以轻松地学习到Linux的基本知识和操作技巧。我相信通过学习这个课程,我会在Linux领域有更进一步的发展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wmh1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值