本文概要:
-
栈的实现
-
括号匹配问题
-
逆波兰表达式求值问题
概述
栈是一种先进后出(FILO)的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读取数据时从栈顶开始弹出数据。
我们称数据进入栈的动作我们称为压栈,数据从栈中出去的动作为弹栈。
栈的实现
public class Stack<T> implements Iterable<T> { /** * 栈空间大小,初始为0 */ private int size; /** * 定义链表表头 */ private Node<T> head; /** * 定义节点类,使用链表的方式实现栈 * @param <T> */ class Node<T> { /** * 数据元素 */ private T item; /** * 下一个节点指针 */ private Node<T> next; public Node(T item,Node<T> next){ this.item = item; this.next = next; } } /** * 栈的构造方法,初始化栈空间大小和head节点 */ public Stack(){ this.size = 0; this.head = new Node<>(null,null); } /** * 获取栈空间元素个数 * @return */ public int size(){ return size; } /** * 判断栈是否为空 * @return */ public boolean isEmpty(){ return size == 0; } /** * 压栈,将节点元素压入栈中 * @param item 节点元素 */ public void push(T item){ //首结点指向的第一个节点 Node<T> oldFirst = head.next; //需要插入的新节点 Node<T> newNode = new Node<>(item,null); //让首结点指向新节点 head.next = newNode; //让新节点指向原来的第一个节点 newNode.next = oldFirst; //元素个数+1 size++; } /** * 弹栈,将第一个元素弹出 * @return */ public T pop(){ //首结点指向的第一个节点 Node<T> oldFirst = head.next; if(oldFirst==null){ return null; } //让首结点指向原来第一个节点的下一个节点 head.next = oldFirst.next; //元素个数-1 size--; return oldFirst.item; } @Override public Iterator<T> iterator() { return new SIterator(); } private class SIterator implements Iterator<T>{ private Node<T> node; public SIterator(){ this.node = head; } @Override public boolean hasNext() { return node.next!=null; } @Override public T next() { node = node.next; return node.item; } } }
测试:
public class StackTest { @Test public void stackTest(){ Stack<String> stack = new Stack<>(); stack.push("a"); stack.push("b"); stack.push("c"); stack.push("d"); for (String s : stack) { System.out.println(s); } System.out.println("-------------"); int size = stack.size(); System.out.println("栈内元素个数:"+size); for (int i = 0; i < size; i++) { System.out.println(stack.pop()); } System.out.println("-------------"); System.out.println(stack.isEmpty()); } }
案例1 括号匹配问题
问题描述:
给定一个字符串,里边可能包含“()”小括号和其他字符,请编写程序检查该字符串中的小括号是否成对出现。 例如: “(上海)(长安)”:正确匹配 “上海((长安))”:正确匹配 “上海(长安(北京)(深圳)南京)”:正确匹配 “上海(长安))”:错误匹配 “((上海)长安”:错误匹配
示例代码:
@Test public void bracketsMatch() { String str1 = "(上海)(长安)"; String str2 = "上海((长安))"; String str3 = "上海(长安(北京)(深圳)南京)"; String str4 = "上海(长安))"; String str5 = "((上海)长安"; System.out.println(isMatch(str1)); System.out.println(isMatch(str2)); System.out.println(isMatch(str3)); System.out.println(isMatch(str4)); System.out.println(isMatch(str5)); } public static boolean isMatch(String str) { //判空处理 if (str == null || str.length() == 0) { return true; } //定义一个栈来存放左括号 Stack<Character> bracket = new Stack<>(); for (int i = 0; i < str.length(); i++) { //遍历字符串,如果遇到左括号就压入栈中 if ('(' == str.charAt(i)) { bracket.push('('); } else if (')' == str.charAt(i)) { //如果遇到右括号就进行弹栈,如果弹栈元素为空则说明括号不匹配 Character pop = bracket.pop(); if (pop == null) { return false; } } } //遍历结束之后看栈中是否还剩余左括号,如果栈为空则说明字符串括号匹配,如果不为空则说明括号不匹配 return bracket.isEmpty(); }
案例2逆波兰表达式求值问题
中缀表达式:
平常生活中使用的表达式,例如:1+3*2,2-(1+3)等等,特点是:二元运算符总是置于两个操作数中间。
逆波兰表达式(后缀表达式):运算符总是放在跟它相关的操作数之后。例如:ab+
中缀表达式 | 逆波兰表达式 |
---|---|
a+b | ab+ |
a+(b-c) | abc-+ |
a+(b-c)*d | abc-d*+ |
需求:
给定一个只包含加减乘除四种运算的逆波兰表达式的数组表示方式,求出该逆波兰表达式的结果。
/** * 案例2:逆波兰表达式 * 给定一个只包含加减乘除四种运算的逆波兰表达式的数组表示方式,求出该逆波兰表达式的结果。 * 中缀表达式:3*(17-15)+18/6 */ @Test public void againstPolandTest() { //中缀表达式:3*(17-15)+18/6,逆波兰表达式如下 String[] notation = {"3", "17", "15", "-", "*", "18", "6", "/", "+"}; //实现caculate方法 int result = caculate(notation); System.out.println("逆波兰表达式的计算结果为:" + result); }
caculate方法的实现
private int caculate(String[] notation) { //定义一个栈来存储操作数 Stack<Integer> stack = new Stack<>(); //从左往右遍历逆波兰表达式,得到每一个元素 for (int i = 0; i < notation.length; i++) { String cuur = notation[i]; //判断当前元素是操作数还是运算符 int t1; int t2; switch (cuur) { //如果是运算符,则从栈中弹出两个元素进行计算,并把计算结果压入栈中 case "+": t1 = stack.pop(); t2 = stack.pop(); stack.push(t2 + t1); break; case "-": t1 = stack.pop(); t2 = stack.pop(); stack.push(t2 - t1); break; case "*": t1 = stack.pop(); t2 = stack.pop(); stack.push(t2 * t1); break; case "/": t1 = stack.pop(); t2 = stack.pop(); stack.push(t2 / t1); break; //如果是操作数,则直接压入栈中 default: stack.push(Integer.parseInt(cuur)); break; } } //最后栈中的数据就是计算结果 return stack.pop(); }
好了,本次内容就是这些,学无止境,关注我,我们一起学习进步。如果觉得内容还可以,帮忙点个赞,点个在看呗,谢谢~我们下期见。