【数据结构-栈】栈基础

news2024/12/26 23:08:13

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
img

  • 推荐:kuan 的首页,持续学习,不断总结,共同进步,活到老学到老
  • 导航
    • 檀越剑指大厂系列:全面总结 java 核心技术点,如集合,jvm,并发编程 redis,kafka,Spring,微服务,Netty 等
    • 常用开发工具系列:罗列常用的开发工具,如 IDEA,Mac,Alfred,electerm,Git,typora,apifox 等
    • 数据库系列:详细总结了常用数据库 mysql 技术点,以及工作中遇到的 mysql 问题等
    • 懒人运维系列:总结好用的命令,解放双手不香吗?能用一个命令完成绝不用两个操作
    • 数据结构与算法系列:总结数据结构和算法,不同类型针对性训练,提升编程思维,剑指大厂

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨

博客目录

    • 一.栈介绍
      • 1.概述
      • 2.链表实现
      • 3.数组实现
      • 4.应用
    • 二.栈题目
      • 1.有效的括号-力扣 20 题
      • 2.后缀表达式求值-力扣 120 题
      • 3.中缀表达式转后缀
      • 4.双栈模拟队列-力扣 232 题
      • 5.单队列模拟栈-力扣 225 题

一.栈介绍

1.概述

计算机科学中,stack 是一种线性的数据结构,只能在其一端添加数据和移除数据。习惯来说,这一端称之为栈顶,另一端不能操作数据的称之为栈底,就如同生活中的一摞书

先提供一个栈接口

public interface Stack<E> {
    /**
     * 向栈顶压入元素
     * @param value 待压入值
     * @return 压入成功返回 true, 否则返回 false
     */
    boolean push(E value);

    /**
     * 从栈顶弹出元素
     * @return 栈非空返回栈顶元素, 栈为空返回 null
     */
    E pop();

    /**
     * 返回栈顶元素, 不弹出
     * @return 栈非空返回栈顶元素, 栈为空返回 null
     */
    E peek();

    /**
     * 判断栈是否为空
     * @return 空返回 true, 否则返回 false
     */
    boolean isEmpty();

    /**
     * 判断栈是否已满
     * @return 满返回 true, 否则返回 false
     */
    boolean isFull();
}

2.链表实现

public class LinkedListStack<E> implements Stack<E>, Iterable<E> {

    private final int capacity;
    private int size;
    private final Node<E> head = new Node<>(null, null);

    public LinkedListStack(int capacity) {
        this.capacity = capacity;
    }

    @Override
    public boolean push(E value) {
        if (isFull()) {
            return false;
        }
        head.next = new Node<>(value, head.next);
        size++;
        return true;
    }

    @Override
    public E pop() {
        if (isEmpty()) {
            return null;
        }
        Node<E> first = head.next;
        head.next = first.next;
        size--;
        return first.value;
    }

    @Override
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return head.next.value;
    }

    @Override
    public boolean isEmpty() {
        return head.next == null;
    }

    @Override
    public boolean isFull() {
        return size == capacity;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            Node<E> p = head.next;
            @Override
            public boolean hasNext() {
                return p != null;
            }

            @Override
            public E next() {
                E value = p.value;
                p = p.next;
                return value;
            }
        };
    }

    static class Node<E> {
        E value;
        Node<E> next;

        public Node(E value, Node<E> next) {
            this.value = value;
            this.next = next;
        }
    }
}

3.数组实现

public class ArrayStack<E> implements Stack<E>, Iterable<E>{
    private final E[] array;
    private int top = 0;

    @SuppressWarnings("all")
    public ArrayStack(int capacity) {
        this.array = (E[]) new Object[capacity];
    }

    @Override
    public boolean push(E value) {
        if (isFull()) {
            return false;
        }
        array[top++] = value;
        return true;
    }

    @Override
    public E pop() {
        if (isEmpty()) {
            return null;
        }
        return array[--top];
    }

    @Override
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return array[top-1];
    }

    @Override
    public boolean isEmpty() {
        return top == 0;
    }

    @Override
    public boolean isFull() {
        return top == array.length;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            int p = top;
            @Override
            public boolean hasNext() {
                return p > 0;
            }

            @Override
            public E next() {
                return array[--p];
            }
        };
    }
}

4.应用

模拟如下方法调用

public static void main(String[] args) {
    System.out.println("main1");
    System.out.println("main2");
    method1();
    method2();
    System.out.println("main3");
}

public static void method1() {
    System.out.println("method1");
    method3();
}

public static void method2() {
    System.out.println("method2");
}

public static void method3() {
    System.out.println("method3");
}

模拟代码

public class CPU {
    static class Frame {
        int exit;

        public Frame(int exit) {
            this.exit = exit;
        }
    }
    static int pc = 1; // 模拟程序计数器 Program counter
    static ArrayStack<Frame> stack = new ArrayStack<>(100); // 模拟方法调用栈

    public static void main(String[] args) {
        stack.push(new Frame(-1));
        while (!stack.isEmpty()) {
            switch (pc) {
                case 1 -> {
                    System.out.println("main1");
                    pc++;
                }
                case 2 -> {
                    System.out.println("main2");
                    pc++;
                }
                case 3 -> {
                    stack.push(new Frame(pc + 1));
                    pc = 100;
                }
                case 4 -> {
                    stack.push(new Frame(pc + 1));
                    pc = 200;
                }
                case 5 -> {
                    System.out.println("main3");
                    pc = stack.pop().exit;
                }
                case 100 -> {
                    System.out.println("method1");
                    stack.push(new Frame(pc + 1));
                    pc = 300;
                }
                case 101 -> {
                    pc = stack.pop().exit;
                }
                case 200 -> {
                    System.out.println("method2");
                    pc = stack.pop().exit;
                }
                case 300 -> {
                    System.out.println("method3");
                    pc = stack.pop().exit;
                }
            }
        }
    }
}

二.栈题目

1.有效的括号-力扣 20 题

一个字符串中可能出现 [] (){} 三种括号,判断该括号是否有效

有效的例子

()[]{}

([{}])

()

无效的例子

[)

([)]

([]

思路

  • 遇到左括号, 把要配对的右括号放入栈顶
  • 遇到右括号, 若此时栈为空, 返回 false,否则把它与栈顶元素对比
    • 若相等, 栈顶元素弹出, 继续对比下一组
    • 若不等, 无效括号直接返回 false
  • 循环结束
    • 若栈为空, 表示所有括号都配上对, 返回 true
    • 若栈不为空, 表示右没配对的括号, 应返回 false

答案(用到了课堂案例中的 ArrayStack 类)

public boolean isValid(String s) {
    ArrayStack<Character> stack = new ArrayStack<>(s.length() / 2 + 1);
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c == '(') {
            stack.push(')');
        } else if (c == '[') {
            stack.push(']');
        } else if (c == '{') {
            stack.push('}');
        } else {
            if (!stack.isEmpty() && stack.peek() == c) {
                stack.pop();
            } else {
                return false;
            }
        }
    }
    return stack.isEmpty();
}

2.后缀表达式求值-力扣 120 题

后缀表达式也称为逆波兰表达式,即运算符写在后面

  • 从左向右进行计算
  • 不必考虑运算符优先级,即不用包含括号

示例

输入:tokens = ["2","1","+","3","*"]
输出:9
即:(2 + 1) * 3

输入:tokens = ["4","13","5","/","+"]
输出:6
即:4 + (13 / 5)

题目假设

  • 数字都视为整数
  • 数字和运算符个数给定正确,不会有除零发生

代码

public int evalRPN(String[] tokens) {
    LinkedList<Integer> numbers = new LinkedList<>();
    for (String t : tokens) {
        switch (t) {
            case "+" -> {
                Integer b = numbers.pop();
                Integer a = numbers.pop();
                numbers.push(a + b);
            }
            case "-" -> {
                Integer b = numbers.pop();
                Integer a = numbers.pop();
                numbers.push(a - b);
            }
            case "*" -> {
                Integer b = numbers.pop();
                Integer a = numbers.pop();
                numbers.push(a * b);
            }
            case "/" -> {
                Integer b = numbers.pop();
                Integer a = numbers.pop();
                numbers.push(a / b);
            }
            default -> numbers.push(Integer.parseInt(t));
        }
    }
    return numbers.pop();
}

3.中缀表达式转后缀

#反编译可以看到执行指令的过程
javap -c -v .\E03InfixToSuffix.class
public class E03InfixToSuffix {
    /*
        思路
        1. 遇到数字, 拼串
        2. 遇到 + - * /
            - 优先级高于栈顶运算符 入栈
            - 否则将栈中高级或平级运算符出栈拼串, 本运算符入栈
        3. 遍历完成, 栈中剩余运算符出栈拼串
            - 先出栈,意味着优先运算
        4. 带 ()
            - 左括号直接入栈
            - 右括号要将栈中直至左括号为止的运算符出栈拼串

        |   |
        |   |
        |   |
        _____

        a+b
        a+b-c
        a+b*c
        a*b+c
        (a+b)*c

     */
    public static void main(String[] args) {
        System.out.println(infixToSuffix("a+b"));
        System.out.println(infixToSuffix("a+b-c"));
        System.out.println(infixToSuffix("a+b*c"));
        System.out.println(infixToSuffix("a*b-c"));
        System.out.println(infixToSuffix("(a+b)*c"));
        System.out.println(infixToSuffix("a+b*c+(d*e+f)*g"));
    }

    static String infixToSuffix(String exp) {
        LinkedList<Character> stack = new LinkedList<>();
        StringBuilder sb = new StringBuilder(exp.length());
        for (int i = 0; i < exp.length(); i++) {
            char c = exp.charAt(i);
            switch (c) {
                case '+', '-', '*', '/' -> {
                    if (stack.isEmpty()) {
                        stack.push(c);
                    } else {
                        if (priority(c) > priority(stack.peek())) {
                            stack.push(c);
                        } else {
                            while (!stack.isEmpty()
                                   && priority(stack.peek()) >= priority(c)) {
                                sb.append(stack.pop());
                            }
                            stack.push(c);
                        }
                    }
                }
                case '(' -> {
                    stack.push(c);
                }
                case ')' -> {
                    while (!stack.isEmpty() && stack.peek() != '(') {
                        sb.append(stack.pop());
                    }
                    stack.pop();
                }
                default -> {
                    sb.append(c);
                }
            }
        }
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }
        return sb.toString();
    }

    static int priority(char c) {
        return switch (c) {
            case '(' -> 0;
            case '*', '/' -> 2;
            case '+', '-' -> 1;
            default -> throw new IllegalArgumentException("不合法字符:" + c);
        };
    }
}

4.双栈模拟队列-力扣 232 题

给力扣题目用的自实现栈,可以定义为静态内部类

class ArrayStack<E> {

    private E[] array;
    private int top; // 栈顶指针

    @SuppressWarnings("all")
    public ArrayStack(int capacity) {
        this.array = (E[]) new Object[capacity];
    }

    public boolean push(E value) {
        if (isFull()) {
            return false;
        }
        array[top++] = value;
        return true;
    }

    public E pop() {
        if (isEmpty()) {
            return null;
        }
        return array[--top];
    }

    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return array[top - 1];
    }

    public boolean isEmpty() {
        return top == 0;
    }

    public boolean isFull() {
        return top == array.length;
    }
}

参考解答,注意:题目已说明

  • 调用 push、pop 等方法的次数最多 100
public class E04Leetcode232 {

    /*
        队列头      队列尾
        s1       s2
        顶   底   底   顶
                 abc

        push(a)
        push(b)
        push(c)
        pop()
     */
    ArrayStack<Integer> s1 = new ArrayStack<>(100);
    ArrayStack<Integer> s2 = new ArrayStack<>(100);

    public void push(int x) {
        s2.push(x);
    }

    public int pop() {
        if (s1.isEmpty()) {
            while (!s2.isEmpty()) {
                s1.push(s2.pop());
            }
        }
        return s1.pop();
    }

    public int peek() {
        if (s1.isEmpty()) {
            while (!s2.isEmpty()) {
                s1.push(s2.pop());
            }
        }
        return s1.peek();
    }

    public boolean empty() {
        return s1.isEmpty() && s2.isEmpty();
    }

}

5.单队列模拟栈-力扣 225 题

给力扣题目用的自实现队列,可以定义为静态内部类

public class ArrayQueue3<E> {

    private final E[] array;
    int head = 0;
    int tail = 0;

    @SuppressWarnings("all")
    public ArrayQueue3(int c) {
        c -= 1;
        c |= c >> 1;
        c |= c >> 2;
        c |= c >> 4;
        c |= c >> 8;
        c |= c >> 16;
        c += 1;
        array = (E[]) new Object[c];
    }

    public boolean offer(E value) {
        if (isFull()) {
            return false;
        }
        array[tail & (array.length - 1)] = value;
        tail++;
        return true;
    }

    public E poll() {
        if (isEmpty()) {
            return null;
        }
        E value = array[head & (array.length - 1)];
        head++;
        return value;
    }

    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return array[head & (array.length - 1)];
    }

    public boolean isEmpty() {
        return head == tail;
    }

    public boolean isFull() {
        return tail - head == array.length;
    }
}

参考解答,注意:题目已说明

  • 调用 push、pop 等方法的次数最多 100
  • 每次调用 pop 和 top 都能保证栈不为空
public class E05Leetcode225 {
    /*
        队列头     队列尾
        cba
        顶           底

        queue.offer(a)
        queue.offer(b)
        queue.offer(c)
     */
    ArrayQueue3<Integer> queue = new ArrayQueue3<>(100);
    int size = 0;
    public void push(int x) {
        queue.offer(x);
        for (int i = 0; i < size; i++) {
            queue.offer(queue.poll());
        }
        size++;
    }

    public int pop() {
        size--;
        return queue.poll();
    }

    public int top() {
        return queue.peek();
    }

    public boolean empty() {
        return queue.isEmpty();
    }
}

觉得有用的话点个赞 👍🏻 呗。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/970583.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java持久化框架JPA,自动执行sql语句的代码实现

在springboot入口处调用&#xff1a; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBoot…

Mac使用VMWare安装centos7回车回退

Mac使用VMWare安装centos7回车回退 说明&#xff1a;本人电脑是MacBook 14pro M2芯片&#xff0c;安装的为VMWare16.2.5&#xff0c;Centos版本为centos stream9 解决方法&#xff1a;使用VM16.2.5Stream9Debian 11操作系统正常安装 问题&#xff1a;下载了VMWare16和Centos…

【微服务部署】五、Jenkins+Docker一键打包部署NodeJS(Vue)项目的Docker镜像步骤详解

NodeJS&#xff08;Vue&#xff09;项目也可以通过打包成Docker镜像的方式进行部署&#xff0c;原理是先将项目打包成静态页面&#xff0c;然后再将静态页面直接copy到Nginx镜像中运行。 一、服务器环境配置 前面说明了服务器Nginx的安装和配置&#xff0c;这里稍微有些不同&a…

java线程和go协程

一、线程的实现 线程的实现方式主要有三种&#xff1a;内核线程实现、用户线程实现、用户线程加轻量级进程混合实现。因为自己只对java的线程比较熟悉一点&#xff0c;所以主要针对java线程和go的协程之间进行一个对比。 线程模型主要有三种&#xff1a;1、内核级别线程&#…

微服务主流框架概览

微服务主流框架概览 目录概述需求&#xff1a; 设计思路实现思路分析1.HSF2.Dubbo 3.Spring Cloud5.gRPC Service mesh 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a be…

深入理解作用域、作用域链和闭包

​ &#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ 目录 &#x1f4da; 前言 &#x1f4d8; 1. 词法作用域 &#x1f4d6; 1.2 示例 &#x1f4d6; 1.3 词法作用域的…

医学专题-多组学在疾病发生发展过程中的研究思路

研究背景 单一组学数据分析通常用来解释某种特征性的生化指标与某些疾病之间的关联&#xff0c;但无法说明其中复杂的因果关系。从疾病表型或某种生物现象出发&#xff0c;寻找影响疾病发生发展的关键因子或通路&#xff0c;借助高通量的技术手段&#xff0c;设置相应的患者组…

49、IDEA 创建类或方法时,实现按格式化 ctrl + alt + l 能变成左花括号在下一行,与右花括号对齐

IDEA 创建类或方法时&#xff0c;左花括号是改成在下一行&#xff0c;与右花括号对齐 默认花括号是这样的 现在想改成这样的 实现按格式化 ctrl alt l 能变成这样 在这里修改就行 把 end of line 改成 next line

基于SSM框架金鱼销售平台源码和论文

基于SSM框架金鱼销售平台源码和论文120 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优…

机器人制作开源方案 | 桌面级全向底盘--本体说明+驱动控制

一、本体说明 1. 底盘概述 该底盘是一款模块化的桌面级应用型底盘&#xff0c;基于应用级软件架构设计、应用级硬件系统设计、典型应用型底盘机械系统设计。 底盘本体为一个采用半独立刚性悬挂的四驱全向底盘。 2. 软件环境介绍 操作系统&#xff1a;Ubuntu18.04系统。基于Deb…

对swap交换分区虚拟内存的理解

Swap分区的作用是什么 更新&#xff1a;2023-05-31 13:10 Swap是一种虚拟内存技术&#xff0c;在计算机内存不足时&#xff0c;它可以将运行中的程序或者数据存到硬盘上以释放内存空间。Swap技术不仅适用于Linux操作系统&#xff0c;Windows和Mac OS也有类似的技术&#xff0…

为什么各个企业都在强调要建立sop?

在现代社会中&#xff0c;随着科技的不断发展&#xff0c;各行各业的竞争也越来越激烈。为了提高工作效率&#xff0c;很多企业开始重视建立标准操作流程&#xff08;SOP&#xff09;。那么&#xff0c;为什么要建立SOP呢&#xff1f; 所谓SOP&#xff0c;就是 Standard Opera…

安装ArcGis时需要安装Micsoft.Net Framework 3.5 sp1

在安转ArcGis时遇到一个问题&#xff0c;解决方法如下 下载.Net 按照他的说明 将地址复制到迅雷中下载&#xff0c;并安装 就可以了 安装就可以了

MES系统在电力装备方面的应用

MES系统主要功能&#xff1a;解决“如何生产”的问题 通过实施MES系统&#xff0c;可以贯通从采购到售后服务的全制造流程&#xff0c;透明化生产现场运作&#xff0c;大大提升了生产制造各部门的管理实时性和有效性。 可获得的效益大致如下&#xff1a; 降低不良率&#xff…

【webrtc】接收/发送的rtp包、编解码的VCM包、CopyOnWriteBuffer

收到的rtp包RtpPacketReceived 经过RtpDepacketizer 解析后变为ParsedPayloadRtpPacketReceived 分配内存,执行memcpy拷贝:然后把 RtpPacketReceived 给到OnRtpPacket 传递:uint8_t* media_payload = media_packet.AllocatePayload(rtx_payload.size());RTC

Python break 语句

Python break语句&#xff0c;就像在C语言中&#xff0c;打破了最小封闭for或while循环。 break语句用来终止循环语句&#xff0c;即循环条件没有False条件或者序列还没被完全递归完&#xff0c;也会停止执行循环语句。 break语句用在while和for循环中。 如果您使用嵌套循环…

关于JDK 8的HashMap

HashMap 简介 HashMap 主要用来存放键值对&#xff0c;它基于哈希表的 Map 接口实现&#xff0c;是常用的 Java 集合之一&#xff0c;是非线程安全的。 HashMap 可以存储 null 的 key 和 value&#xff0c;但 null 作为键只能有一个&#xff0c;null 作为值可以有多个 JDK1.8…

信号浪涌保护器防雷接地工程应用方案

信号浪涌保护器是一种用于保护电子设备免受电力线路上的瞬时过电压或过电流的装置。信号浪涌保护器的参数方案和应用施工主要取决于信号线路的类型、电气特性、工作环境和保护要求。下面是一篇关于信号浪涌保护器的文章&#xff0c;介绍了一些常见的信号浪涌保护器参数方案和应…

Android Canvas的使用

android.graphics.Canvas 一般在自定义View中&#xff0c;重写 onDraw(Canvas canvas) 方法时用到。 /*** Implement this to do your drawing.** param canvas the canvas on which the background will be drawn*/Overrideprotected void onDraw(Canvas canvas) {super.onDra…

LeetCode 剑指 Offer 10- I. 斐波那契数列

LeetCode 剑指 Offer 10- I. 斐波那契数列 题目描述 写一个函数&#xff0c;输入 n &#xff0c;求斐波那契&#xff08;Fibonacci&#xff09;数列的第 n 项&#xff08;即 F(N)&#xff09;。斐波那契数列的定义如下&#xff1a; F(0) 0, F(1) 1 F(N) F(N - 1) F(N - …