数据结构之栈的基本知识与习题

本文介绍了栈这一数据结构的基本概念,包括压栈和出栈操作。详细阐述了栈在改变元素序列、逆序打印链表、括号匹配、逆波兰式求值以及判断栈的弹出序列等方面的典型应用场景,并提供了相关代码示例。此外,还提到了栈的简单数组实现,以及与栈帧和虚拟机栈的区别。
摘要由CSDN通过智能技术生成

目录

1.有关概念

2.基本方法和使用场景

2.1.方法

2.2.使用

2.3.应用场景

2.3.1.改变元素的序列

2.3.2.逆序打印链表

2.3.3.oj题括号匹配

2.3.4.逆波兰式求值

2.3.5.栈的弹出序列

3.栈的简单实现(数组实现)

4.有关栈的概念区分


1.有关概念

        栈:一种特殊的线性表,其 只允许在固定的一端进行插入和删除元素操作 。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出 的原则。
        压栈:栈的插入操作叫做进栈/ 压栈 / 入栈, 入数据在栈顶,入栈后的数据在栈底
        出栈:栈的删除操作叫做出栈,出数据在栈顶,准备出栈的数据在栈顶

2.基本方法和使用场景

        栈的结构就像手枪,push方法相当于装子弹,先装的子弹后打出,最后装的子弹先打出。pop方法就是射击,将弹夹最上面的子弹射出。在手枪射击的过程中,我们也可以随时补充子弹,同时子弹的射出顺序也就发生了改变,这就说明栈的重要应用场景就是改变数据序列的顺序。

2.1.方法

方法功能
Stack()
构造一个空的栈
E push(E e)
e 入栈,并返回 e
E pop()
将栈顶元素出栈并返回
E peek()
获取栈顶元素
int size()
获取栈中有效元素个数
boolean empty()
检测栈是否为空

2.2.使用

public static void main(String[] args) { 
    Stack<Integer> s = new Stack(); 
    s.push(1); 
    s.push(2); 
    s.push(3); 
    s.push(4); 
    System.out.println(s.size()); // 获取栈中有效元素个数---> 4                     
    System.out.println(s.peek()); // 获取栈顶元素---> 4 
    s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3 
    System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3 
    if(s.empty()){ 
        System.out.println("栈空"); 
    }else{ 
        System.out.println(s.size()); 
    }    
}

2.3.应用场景

2.3.1.改变元素的序列

1. 若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是(C)
A: 1,4,3,2       B: 2,3,4,1      C: 3,1,4,2      D: 3,4,2,1
解析:入栈的相对顺序是固定的,因此想要出栈某个数据,前提是这个数据刚放进去。A选项:先入栈1,出栈1,依次入栈2、3、4,依次出栈4、3、2;B选项:先依次入栈1、2,出栈2,再入栈3,出栈3,入栈4,出栈4,最后出栈1;C选项:要想3先出栈,就需要依次入栈1、2、3,然后3出栈,要想1出栈,就必须2先出栈,故该顺序不可能;D选项:先入栈1、2、3,出栈3,入栈4,出栈4,最后依次出栈2、1。

2.3.2.逆序打印链表

// 递归方式 
void printList(Node head){ 
    if(null != head){
        printList(head.next); 
        System.out.print(head.val + " "); 
    } 
}
// 循环方式 
void printList(Node head){ 
    if(null == head){ 
        return; 
    }
    Stack<Node> s = new Stack<>(); // 将链表中的结点保存在栈中 
    Node cur = head; 
    while(null != cur){ 
        s.push(cur);
        cur = cur.next; 
    }    
    // 将栈中的元素出栈 
    while(!s.empty()){ 
        System.out.print(s.pop().val + " "); 
    } 
}

2.3.3.oj题括号匹配

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for(int i = 0;i<s.length();i++){
            char ch = s.charAt(i);
            if(ch == '('||ch == '['||ch == '{'){
                //左括号入栈
                stack.push(ch);
            }else{
                //右括号多
                if(stack.empty()){
                    return false;
                }
                //右括号取出栈顶元素匹配
                char c = stack.peek();
                //括号不匹配
                if((c == '('&&ch==')')||(c == '['&&ch==']')||(c == '{'&&ch=='}')) {
                    stack.pop();
                }else{
                    return false;
                }
            }
        }
        //遍历结束栈中还有元素说明左括号多
        if(!stack.empty()){
            return false;
        }
        return true;
    }

图解:

2.3.4.逆波兰式求值

        根据逆波兰表示法,求表达式的值。有效的算符包括 +-*/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for(String x:tokens){
            //如果是数字字符串
            if(!isOperation(x)){
                //将数字字符串转为数字压入栈
                stack.push(Integer.parseInt(x));
            }else{
                //如果是运算符字符串,将顶两个数据出栈运算
                int num1 = stack.pop();
                int num2 = stack.pop();
                switch (x){
                    case "+":
                        stack.push(num2+num1);
                        break;
                    case "-":
                        stack.push(num2-num1);
                        break;
                    case "*":
                        stack.push(num2*num1);
                        break;
                    case "/":
                        stack.push(num2/num1);
                        break;
                }
            }
        }
        return stack.pop();
    }
    //判断是否是运算符
    private boolean isOperation(String s) {
        if(s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/")) {
            return true;
        }
        return false;
    }

图解:

2.3.5.栈的弹出序列

        输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

    public boolean IsPopOrder(int [] pushA,int [] popA) {
        Stack<Integer> stack = new Stack<>();
        int j = 0;
        for(int i = 0;i<pushA.length;i++){
            stack.push(pushA[i]);
            while(j<popA.length&&!stack.empty()&&stack.peek() == popA[j]){
                stack.pop();
                j++;
            }
        }
        return stack.empty();
    }

 解析:pushA数组是压栈顺序,popA数组是需要判断的弹出序列,没有遇到出栈数据前,将数据依次压入stack栈中,当stack栈顶数据和需要出栈的数据相同时,将stack栈的栈顶元素出栈,遍历完pushA数组,如果stack栈不为空,说明不为弹出序列;栈为空,说明符合栈的压栈和出栈特点。

3.栈的简单实现(数组实现)

import java.util.Arrays;


public class MyStack {
    public int[] elem;
    public int usedSize;
    //记录当前元素个数,还是放元素时数组的下标
    public MyStack() {
        this.elem = new int[10];
    }

    public void push(int val){
        if(isFull()){
            this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
        }
        this.elem[this.usedSize] = val;
        this.usedSize++;
    }
    //判断是否为满
    public boolean isFull(){
        return this.usedSize == this.elem.length;
    }
    //出栈
    public int pop() {
        if(empty()){
            throw new RuntimeException("栈为空");
        }
        int val = this.elem[this.usedSize-1];
        this.usedSize--;
        return val;
    }
    //获取栈顶元素
    public int peek() {
        if(empty()){
            throw new RuntimeException("栈为空");
        }
        return this.elem[this.usedSize-1];
    }
    //判断是否为空
    public boolean empty() {
        return this.usedSize == 0;
    }
    //栈元素个数
    public int size(){
        return this.usedSize;
    }

}

4.有关栈的概念区分

栈:一种数据结构,是一种线性表,数据遵循先进后出。

栈帧:方法的运行空间,每个方法都有属于自己的栈帧,每个栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向运行时常量的引用( the run-time constant pool)、方法返回地址(Return Address)和附加信息。

虚拟机栈:JVM虚拟机用来储存栈帧的数据结构,虚拟机栈中的数据元素就是虚拟机运行时开辟的栈帧元素。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值