栈
概述
生活中的栈 客栈
计算机中的栈
我们把生活中的栈的概念引入到计算机中,就是供数据休息的地方它是一种数据结构,数据既可以进入到栈中,又可以从栈中出
去。
栈是一种基于先进后出(FILO)的教据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储教据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来 )。我们称数据进入到栈的动作为压栈,数据从栈中出去的动作为弹栈。
栈的实现
我们采用链表来实现栈结构
方法:栈是否为空 isEmpty()、获取栈中的元素个数size()、弹栈pop() 、压栈 push、遍历
因为需要使用链表来实现,所以 需要head节点和Node节点类
Java里面有对应的实现Stack类
package com.d318;
import java.util.EmptyStackException;
public class Stack {
private Node head;
private int count;
// 构造方法的创建
public Stack() {
this.head = new Node(null, null);
this.count = 0;
}
// 获取栈中元素个数
public int size() {
return count;
}
// 获取栈是否为空
public boolean isEmpty() {
return count == 0;
}
// 入栈
public void push(String data) {
Node newNode = new Node(data, null);
Node next = head.next;
head.next = newNode;
newNode.next = next;
count++;
}
// 弹栈 按照FILO原则,先进的后出,后进的先出
public String pop() {
if (count == 0)
throw new EmptyStackException();
Node target = head.next;
Node next = target.next;
head.next = next;
count--;
return target.data;
}
public void printStack(){
Node cur = head.next;
while(cur!=null){
System.out.println(cur.data);
cur = cur.next;
}
}
public static void main(String[] args) {
Stack stack = new Stack();
stack.push("aaa");
stack.push("bbb");
stack.push("ccc");
stack.push("ddd");
stack.push("eee");
stack.push("fff");
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.isEmpty());
System.out.println(stack.size());
stack.printStack();
}
}
class Node {
public String data;
public Node next;
public Node(String data, Node next) {
this.data = data;
this.next = next;
}
}
栈的应用案例
括号匹配问题
问题描述:
给定一个字符串,里边可能包含”()“小括号和其他字符,请编写程序检查该字符串的中的小括号是否成对出现
例如 :
(上海)(长安): 正确匹配
上海((长安)): 正确匹配
上海(长安(北京)(深圳)南京):正确匹配
上海(长安)): 错误匹配
((上海)长安: 错误匹配
思路:遇到左括号就入栈,遇到右括号就将栈里的左括号弹出与之匹配;
如果遇到右括号,栈中没有与之匹配的左括号,则说明括号不匹配;
如果遍历完了整个字符串,也就是没有右括号了,但是此时栈中还有左括号等待被匹配,那就说明左括号数量大于右括号数量,括号数量也不匹配;
如果遍历完整个字符串,栈中也没有左括号等待被匹配,此时正好匹配;
public static boolean isMatch(String str) {
Stack stack = new Stack(); // 创建栈对象,这里使用了String类型,而非默认的Object
for (int i = 0; i < str.length(); i++) { // 这里原代码中的'日'应该为'0'
char c = str.charAt(i);
if (c == '(') {
// 遇见左括号就压入栈中
stack.push("" + c); // 此处直接将字符转为字符串入栈,也可以省略+""
} else if (c == ')') { // 漏掉了else if,来确保仅对右括号处理
// 遇见右括号就将栈中的左括号弹出与之匹配
if (stack.isEmpty()) {
return false; // 栈为空,说明右括号数量大于左括号数量
}
stack.pop(); // 弹出栈顶元素
}
}
// 字符串遍历完,检查栈中是否还有剩余左括号
if (!stack.isEmpty()) {
return false; // 栈中仍有左括号,说明左括号数量大于右括号数量
}
return true; // 栈为空,表示所有括号都已正确匹配
}
逆波兰表达式求值问题
计算中缀表达式 3*(17-15)+18/6的逆波兰表达式的结果值
思路:遍历数组,如果元素是数据,直接入栈,是符号就从栈中弹出两个值进行计算,计算完后的结果入栈,遍历完数组之后就将最后的结果弹栈
代码:
package com.d318;
public class ReversePoLoTest {
public static void main(String[] args) {
// 计算中缀表达式 3*(17-15)+18/6的逆波兰表达式的结果值
String[] notation = {"3", "17", "15", "-", "*", "18", "6", "/", "+"};
int result = caculate(notation);
System.out.println(result);
}
public static int caculate(String[] notation) {
Stack stack = new Stack();
for (String s : notation) {
// 如果s是数据,直接入栈,是符号就从栈中弹出两个值进行计算,计算完后的结果入栈
int a, b,result;
switch (s) {
case "+":
a = Integer.valueOf(stack.pop());
b = Integer.valueOf(stack.pop());
result = a+b;
stack.push(result + "");
break;
case "-":
a = Integer.valueOf(stack.pop());
b = Integer.valueOf(stack.pop());
result = b-a;
stack.push(result + "");
break;
case "*":
a = Integer.valueOf(stack.pop());
b = Integer.valueOf(stack.pop());
result = a*b;
stack.push(result + "");
break;
case "/":
a = Integer.valueOf(stack.pop());
b = Integer.valueOf(stack.pop());
result = b/a;
stack.push(result + "");
break;
default:
stack.push(s);
break;
}
}
return Integer.valueOf(stack.pop());
}
}