前言
hello,大家好,我是阿旭啊。今天给大家带来数据结构第七篇:栈。希望能给大家一些小帮助~
一.栈的介绍
1.什么是栈
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守先进后出的原则 。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶 。
栈在现实中的例子:
2.栈的方法:
代码演示:
public static void main(String[] args) {
System.out.println("1.=================================");
Stack<Integer> stack = new Stack();
//压栈
stack.push(12);
stack.push(23);
stack.push(34);
stack.push(45);
System.out.println(stack);
System.out.println("2.=================================");
//出栈
stack.pop();
System.out.println(stack);
//由此可见先入后出
System.out.println("3.=================================");
//获取栈顶数据,但是不出栈
int a = stack.peek();
System.out.println(a);
System.out.println("4.=================================");
//获取栈数据个数
int size = stack.size();
System.out.println(size);
System.out.println("5.=================================");
//判断是否栈空
System.out.println(stack.empty());
System.out.println("6.=================================");
}
运行结果:
1.=================================
[12, 23, 34, 45]
2.=================================
[12, 23, 34]
3.=================================
34
4.=================================
3
5.=================================
false
6.=================================
3.栈的模拟
在java原码中栈的底层是一个数组,所以我们可以使用数组进行站的模拟,当然也可以使用链表。
这里我们使用数组进行模拟。
栈类:
public class MyStack {
private int[] elem;
private int usedSize;
public MyStack(){
this.elem = new int[10];
}
public void show(){
for (int i = 0; i < this.usedSize; i++){
System.out.print(this.elem[i] + " ");
}
System.out.println();
}
//1.压栈
public void push(int value){
if (this.elem.length == this.usedSize){
this.elem = Arrays.copyOf(this.elem, this.usedSize * 2);
}
this.elem[usedSize] = value;
usedSize ++;
}
//2.判空
public boolean empty(){
return this.usedSize == 0;
}
//3.出栈
public int pop(){
if (empty()){
throw new StackEmptyException("栈为空!!!");
}
int a = this.elem[this.usedSize -1];
usedSize--;
return a;
}
//4.获取栈顶数据
public int peek(){
if (empty()){
throw new StackEmptyException("栈为空!!!");
}
return this.elem[this.usedSize - 1];
}
//5.获取栈的数据个数
public int size(){
return this.usedSize;
}
}
如果大家把顺序表搞明白了还是非常简单的!!!
自定义异常:
public class StackEmptyException extends RuntimeException{
public StackEmptyException(){
}
public StackEmptyException(String message){
super(message);
}
}
代码测试:
public static void main(String[] args) {
System.out.println("1.=================================");
MyStack stack = new MyStack();
//压栈
stack.push(12);
stack.push(23);
stack.push(34);
stack.push(45);
stack.show();
System.out.println("2.=================================");
//出栈
System.out.println(stack.pop());
stack.show();
//由此可见先入后出
System.out.println("3.=================================");
//获取栈顶数据,但是不出栈
System.out.println(stack.peek());
stack.show();
System.out.println("4.=================================");
//获取栈数据个数
System.out.println(stack.size());
System.out.println("5.=================================");
//判断是否栈空
System.out.println(stack.empty());
System.out.println("6.=================================");
}
测试结果:
1.=================================
12 23 34 45
2.=================================
45
12 23 34
3.=================================
34
12 23 34
4.=================================
3
5.=================================
false
6.=================================
二.栈的使用场景
1.链表逆序打印
(1)递归方法一:
public void show1(ListNode head){
if (head != null){
show1(head.next);
System.out.print(head.value + " ");
}
}
(2)递归方法二:
public void show2(ListNode head){
if (head == null){
return;
}
if (head.next == null){
System.out.println(head.value);
return;
}
show2(head.next);
System.out.println(head.value);
}
(3)循环方法:
public void show3(ListNode head){
Stack<ListNode> stack = new Stack<>();
ListNode cur = head;
//将节点放到栈中
while(cur != null){
stack.push(cur);
cur = cur.next;
}
//将节点一个一个出栈
while (!stack.empty()){
ListNode node = stack.pop();
System.out.println(node.value);
}
}
2.逆波兰表达式
首先要了解中缀表达式和后缀表达式。
后缀表达式就是把符号放到数字的后面。
将中缀表达式转换成后缀表达式:
如:a+bc+(de+f)g
将后缀表达式转换成中缀表达式:
如abc+def+g+
思路:将每个字符放到栈中,若为运算符则弹出两个数据分别为右操作数和左操作数,然后将算出来的数据再放到栈中,直到所有字符都弹出过一次
逆波兰表达式就是后缀表达式,本题是输入一个逆波兰表达式计算其值。
思路:利用栈进行计算。遍历数组,如果字符是数字类型就转换成int类型然后压栈,如果是运算符则弹出两个栈中数据进行相应的计算(注意左右操作数顺序要对。)然后在进行压栈。计算完毕后返回栈中的数据(此时也只有一个数据了。)
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].equals("+") || tokens[i].equals("-") || tokens[i].equals("*") || tokens[i].equals("/")) {
int num2 = stack.pop();
int num1 = stack.pop();
switch (tokens[i]) {
case "+":
stack.push(num1 + num2);
break;
case "-":
stack.push(num1 - num2);
break;
case "*":
stack.push(num1 * num2);
break;
case "/":
stack.push(num1 / num2);
break;
}
} else {
stack.push(Integer.parseInt(tokens[i]));
}
}
return stack.pop();
}
3.括号匹配问题
思路:如果是左括号则放到栈中,如果是有括号将栈顶元素弹出并判断匹配。
第一种情况:(){}[] 完美匹配
第二种情况:([)] 不匹配
第三种情况:(() 匹配但是字符串遍历完了,栈还没空
第四种情况:()) 匹配但是栈空了,字符串没有遍历完
只有第一种情况为true,其余情况为false
请看代码:
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
//遍历字符串
for (int i = 0; i < s.length(); i++){
//判断是否为左括号
if (s.charAt(i) == '{' || s.charAt(i) == '[' || s.charAt(i) == '('){
//左括号压栈
stack.push(s.charAt(i));
}else{
//判断是否栈空了但是字符串没有遍历完
if (stack.empty()){
return false;
}
//匹配有括号
char a = stack.pop();
switch(s.charAt(i)){
case '}':
if (a != '{'){
return false;
}
break;
case ']':
if (a != '['){
return false;
}
break;
case ')':
if (a != '('){
return false;
}
break;
}
}
}
//判断是否字符串遍历完,栈还没空
if (!stack.empty()){
return false;
}
return true;
}
4.出栈入栈次序匹配
我们可以设置一个真实栈和一个最小值栈,最小栈的栈顶元素是真实栈的元素的最小值。
push方法:一开始两个栈都要压栈。真实栈直接压,当最小栈为空时也直接压。当最小栈不为空的时候,如果最小栈的栈顶元素>=压栈元素时,最小值栈也压栈,其他情况不压栈。
public void push(int val) {
stack.push(val);
if (minstack.empty()){
minstack.push(val);
}else{
if (val <= minstack.peek()){
minstack.push(val);
}
}
}
pop方法:首先判空,如果真实栈空就直接返回。真实栈直接弹出。当弹出元素与最小栈的栈顶元素相等时候,最小栈也弹出
public void pop() {
if (stack.empty()){
return;
}
int val = stack.pop();
if (val == minstack.peek()){
minstack.pop();
}
}
全部代码:
class MinStack {
private Stack<Integer> stack;
private Stack<Integer> minstack;
public MinStack() {
this.stack = new Stack<>();
this.minstack = new Stack<>();
}
public void push(int val) {
stack.push(val);
if (minstack.empty()){
minstack.push(val);
}else{
if (val <= minstack.peek()){
minstack.push(val);
}
}
}
public void pop() {
if (stack.empty()){
return;
}
int val = stack.pop();
if (val == minstack.peek()){
minstack.pop();
}
}
public int top() {
if (stack.empty()){
return -1;
}
return stack.peek();
}
public int getMin() {
return minstack.peek();
}
}
总结
好啦~本篇文章就到此结束。希望大家能够多多点赞,多多评论,多多批评,大家下一篇再见!!!