Java数据结构6-栈与队列

1. 栈(Stack)

1.1 概念

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则

  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
  • 出栈:栈的删除操作叫做出栈。出数据在栈顶

在这里插入图片描述

栈在现实生活中的例子:

在这里插入图片描述

1.2 栈的使用


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

1.3 栈的模拟实现

在这里插入图片描述

从上图中可以看到,Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的。

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()) {
            elem = Arrays.copyOf(elem, 2*elem.length);
        }
        elem[usedSize] = val;
        usedSize++;
    }

    public boolean isFull() {
        return usedSize == elem.length;
    }

    public int pop() {
        if( empty() ){
            throw new RuntimeException("栈为空,无法删除元素");
        }
        int oldVal = elem[usedSize-1];
        usedSize--;
        return oldVal;
    }

    public boolean empty() {
        return usedSize == 0;
    }

    public int peek() {
        if(empty()){
            throw new RuntimeException("栈为空,无法获取栈顶元素");
        }

        return elem[usedSize-1];
    }


}
public class Test {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        myStack.push(1);
        myStack.push(2);
        myStack.push(3);

        System.out.println(myStack.pop());
        System.out.println(myStack.peek());
    }

}

1.4 栈的应用场景

  1. 改变元素的序列
1. 若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
A: 1,4,3,2 B: 2,3,4,1 C: 3,1,4,2 D: 3,4,2,1

C:输出3后不能跳过2就输出1

2.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺
序是( )。
A: 12345ABCDE B: EDCBA54321 C: ABCDE12345 D: 54321EDCBA

B
  1. 将递归转化为循环

比如:逆序打印链表

// 递归方式
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 + " ");
  }
}
  1. 逆波兰表达式求值

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。

import java.util.Stack;

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> st = new Stack<>();
        for (String a:
             tokens) {
            if (a.equals("+")) {
                Integer num1 = st.pop();
                Integer num2 = st.pop();
                st.push(num2+num1);
            } else if (a.equals("-")) {
                Integer num1 = st.pop();
                Integer num2 = st.pop();
                st.push(num2-num1);
            } else if (a.equals("*")) {
                Integer num1 = st.pop();
                Integer num2 = st.pop();
                st.push(num2*num1);
            } else if (a.equals("/")) {
                Integer num1 = st.pop();
                Integer num2 = st.pop();
                st.push(num2/num1);
            } else {
                st.push(Integer.valueOf(a));
            }
        }
        return st.peek();
    }
}
  1. 有效的括号

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

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。

import java.util.Stack;

class Solution {
    public boolean isValid(String s) {
        Stack<Character> sk = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (ch == '(' || ch == '{' || ch == '[') {
                sk.push(ch);
            } else {
                if (sk.isEmpty()) {
                    return false;
                } else {
                    char chL = sk.peek();
                    if (chL == '(' && ch == ')' || chL == '{' && ch == '}'
                            ||chL == '[' && ch == ']') {
                         sk.pop();
                    } else {
                        return false;
                    }
                }
            }
        }
        return sk.empty();
    }
}
  1. 栈的压入、弹出序列

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

  1. 0<=pushV.length == popV.length <=1000
  2. -1000<=pushV[i]<=1000
  3. pushV 的所有数字均不相同
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pushV int整型一维数组 
     * @param popV int整型一维数组 
     * @return bool布尔型
     */
    
    public boolean IsPopOrder (int[] pushV, int[] popV) {
        // write code here
        int j = 0;
        Stack<Integer> sk = new Stack<>();
        for (int i = 0; i < pushV.length; i++) {
            sk.push(pushV[i]);
            while (j < popV.length && sk.peek() == popV[j]
                    && !sk.empty()) {
                sk.pop();
                j++;
            }
        }
        return sk.empty();
    }
}
  1. 最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。

import java.util.Stack;

class MinStack {


    Stack<Integer> stack;
    Stack<Integer> minStack;
    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
    
    public void push(int val) {
        stack.push(val);
        if (minStack.empty()) {
            minStack.push(val);
        } else {
            Integer peekVal = minStack.peek();
            if (val <= peekVal) {
                minStack.push(val);
            }
        }
    }
    
    public void pop() {
        if (stack.empty()) {
            return;
        }
        Integer popVal = stack.pop();
        if (popVal.equals(minStack.peek())) {
            minStack.pop();
        }
        
    }
    
    public int top() {
        if (stack.empty()) {
            return -1;
        } 
        return stack.peek();
    }
    
    public int getMin() {
        if (minStack.empty()) {
            return -1;
        } 
        return minStack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

2. 队列(Queue)

2.1 概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)

在这里插入图片描述

2.2 队列的使用

在Java中,Queue是个接口,底层是通过链表实现的。

在这里插入图片描述

方法功能
boolean offer(E e)入队列
E poll()出队列
E peek()获取队头元素
int size()获取队列中有效元素个数
boolean isEmpty()检测队列是否为空

注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。

public static void main(String[] args) {
  Queue<Integer> q = new LinkedList<>();
  q.offer(1);
  q.offer(2);
  q.offer(3);
  q.offer(4);
  q.offer(5); // 从队尾入队列
  System.out.println(q.size());
  System.out.println(q.peek()); // 获取队头元素
  q.poll();
  System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
  if(q.isEmpty()){
    System.out.println("队列空");
  }else{
    System.out.println(q.size());
  }
}

2.3 队列模拟实现

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有两种:顺序结构 和 链式结构。

队列的实现使用顺序结构还是链式结构好?

public class MyQueue {

    static class ListNode {
        public int val;
        public ListNode prev;
        public ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode head;
    public ListNode last;

    public void offer(int val) {
        ListNode node = new ListNode(val);
        if (head == null) {
            head = last = node;
        } else {
            last.next = node;
            node.prev = last;
            last = last.next;
        }
    }

    public int poll() {
        if (head == null) {
            return -1;
        }
        int ret = head.val;
        if (head.next == null) {
            head = null;
            last = null;
        } else {
           head = head.next;
           head.prev = null;
        }
        return ret;
    }

    public int peek() {
        if (head == null) {
            return -1;
        }
        return head.val;
    }

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

public class Test {
    public static void main(String[] args) {
        MyQueue myQueue = new MyQueue();
        myQueue.offer(1);
        myQueue.offer(2);
        myQueue.offer(3);

        System.out.println(myQueue.poll());
        System.out.println(myQueue.peek());
    }

}

2.4 循环队列

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。环形队列通常使用数组实现。

在这里插入图片描述

2.4.1 数组下标循环的小技巧

  1. 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length

在这里插入图片描述

  1. 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset) % array.length

在这里插入图片描述

2.4.2 如何区分空与满

  1. 通过添加 size 属性记录
  2. 保留一个位置
  3. 使用标记

2.4.3 设计循环队列

class MyCircularQueue {
    public int[] elem;
    public int first;
    public int last;

    public MyCircularQueue(int k) {
        elem = new int[k+1];
    }
    
    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }
        elem[last] = value;
        last = (last+1) % elem.length;
        return true;
    }
    
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }
        first = (first+1) % elem.length;
        return true;
    }
    
    public int Front() {
        if (isEmpty()) {
            return -1;
        }
        return elem[first];
    }
    
    public int Rear() {
        if (isEmpty()) {
            return -1;
        }
        if (last == 0) {
            return elem[elem.length-1];
        } else {
            return elem[last-1];
        }
    }
    
    public boolean isEmpty() {
        return first == last;
    }
    
    public boolean isFull() {
        return (last+1) % elem.length == first;
    }
}

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue obj = new MyCircularQueue(k);
 * boolean param_1 = obj.enQueue(value);
 * boolean param_2 = obj.deQueue();
 * int param_3 = obj.Front();
 * int param_4 = obj.Rear();
 * boolean param_5 = obj.isEmpty();
 * boolean param_6 = obj.isFull();
 */

3. 双端队列 (Deque)

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

在这里插入图片描述

Deque是一个接口,使用时必须创建LinkedList的对象。

在这里插入图片描述

在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口。

Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现

4. 面试题

1. 用队列实现栈

import java.util.LinkedList;
import java.util.Queue;

class MyStack {
    public Queue<Integer> queue1;
    public Queue<Integer> queue2;

    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    public void push(int x) {
        if (empty()) {
            queue1.offer(x);
            return;
        }
        if (!queue1.isEmpty()) {
            queue1.offer(x);
        } else {
            queue2.offer(x);
        }
    }
    
    public int pop() {
        if (empty()) {
            return -1;
        }
        if (!queue1.isEmpty()) {
            int size = queue1.size();
            for (int i = 0; i < size-1; i++) {
                queue2.offer(queue1.poll());
            }
            return queue1.poll();
        } else {
            int size = queue2.size();
            for (int i = 0; i < size-1; i++) {
                queue1.offer(queue2.poll());
            }
            return queue2.poll();
        }
    }
    
    public int top() {
        if (empty()) {
            return -1;
        }
        if (!queue1.isEmpty()) {
            int ret = -1;
            int size = queue1.size();
            for (int i = 0; i < size; i++) {
                ret = queue1.poll();
                queue2.offer(ret);
            }
            return ret;

        } else {
            int ret = -1;
            int size = queue2.size();
            for (int i = 0; i < size; i++) {
                ret = queue2.poll();
                queue1.offer(ret);
            }
            return ret;
        } 
    }
    
    public boolean empty() {
        return queue1.isEmpty() && queue2.isEmpty();
    }
}
/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

2. 用栈实现队列

import java.util.Stack;

class MyQueue {

    public Stack<Integer> stack1;
    public Stack<Integer> stack2;
    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
    
    public void push(int x) {
        stack1.push(x);
    }
    
    public int pop() {
        if (empty()) {
            return -1;
        }
        if (stack2.empty()) {
            while (!stack1.empty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();

    }
    
    public int peek() {
        if (empty()) {
            return -1;
        }
        if (stack2.empty()) {
            while (!stack1.empty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }
    
    public boolean empty() {
        return stack1.empty() && stack2.empty();
    }
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * boolean param_4 = obj.empty();
 */

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

jenkins 发布服务到 windows服务器

1.环境准备 1.1 这些就不过多描述了&#xff0c;可以参考我的另一盘文章部署到linux。 jenkins 发布服务到linux服务器-CSDN博客 1.2 需要再windows上安装openssh 地址&#xff1a;Releases PowerShell/Win32-OpenSSH GitHub 到windows上执行安装&#xff0c;可以里面cmd命令…

黄冈师范学院2024年成人高等继续教育招生简章

黄冈师范学院&#xff0c;这座矗立在湖北黄冈的教育殿堂&#xff0c;以其深厚的文化底蕴和卓越的教学质量&#xff0c;吸引了无数求学者。如今&#xff0c;随着社会的快速发展和教育的不断进步&#xff0c;黄冈师范学院再次敞开怀抱&#xff0c;热烈迎接2024年成人高等继续教育…

LeetCode 算法:二叉树的右视图 c++

原题链接&#x1f517;&#xff1a;二叉树的右视图 难度&#xff1a;中等⭐️⭐️ 题目 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4…

【ArcGIS AddIn插件】【可用于全国水旱灾害风险普查】全网最强洪水淹没分析插件-基于8邻域种子搜索算法-有源淹没分析算法

最近有很多GIS小伙伴咨询我关于基于8邻域种子搜索算法的有源淹没分析插件的使用方法及原理&#xff0c;咱们通过这篇文章给大家详细介绍下这款插件的运行机制。 一、插件类型及适用版本 本插件属于ArcGIS AddIn工具条插件&#xff0c;基于ArcGIS Engine10.2.2的开发环境开发的&…

Nature:使用语义熵检测大语言模型中的幻觉

使用语义熵检测大语言模型中的幻觉 Detecting hallucinations in large language models using semantic entropy 论文阅读摘要研究目标论文图表概述总结关键解决方案语义熵计算:虚构内容检测: 双向蕴涵在大语言模型中的应用上下文的重要性蕴涵估计器 实验设计语义熵计算步骤结…

Firewalld防火墙基础

Firewalld 支持网络区域所定义的网络连接以及接口安全等级的动态防火墙管理工具 支持IPv4、IPv6防火墙设置以及以太网桥 支持服务或应用程序直接添加防火墙规则接口 拥有两种配置模式 运行时配置&#xff1a;临时生效&#xff0c;一旦重启或者重载即不生效 永久配置&#xff1a…

【简易版tinySTL】 红黑树- 定义, 插入, 构建

文章目录 旋转左旋右旋 左旋右旋代码实现红黑树的基本性质红黑树的插入红黑树的插入示例红黑树修复代码实现参考资料 旋转 对于一个平衡二叉搜索树&#xff0c;左子树高度为4&#xff0c;右子树高度为2&#xff0c;它们的高度差为2&#xff0c;破坏了平衡性&#xff08;高度差&…

IT之家最新科技热点

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

前端:Nuxt2 + Vuetify2

想要开发一个网站&#xff0c;并且支持SEO搜索&#xff0c;当然离不开我们的 Nuxt &#xff0c;那通过本篇文章让我们一起了解一下。如果构建一个Nuxt项目 安装 Nuxt&#xff0c;创建项目 安装nuxt2&#xff0c; 需要node v16&#xff0c;大家记得查看自己的node版本。构建脚…

初阶数据结构之堆讲解

本篇文章带大家学习的是堆&#xff0c;还请各位观众老爷给个三连 正片开始 堆的概念 如果有一个关键码的集合 K { &#xff0c; &#xff0c; &#xff0c; … &#xff0c; } &#xff0c;把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中&#xff0c;并满…

J021_QQ号格式校验

一、需求描述 校验QQ号码是否正确。要求全部是数字&#xff0c;数字长度&#xff08;6-20位之间&#xff09;&#xff0c;不能以0开头。 二、代码实现 package com.itheima.sort;public class Test {public static void main(String[] args) {System.out.println("----…

MySQL4(事务、函数、慢查询和索引)

目录 一、MySQL事务 1. 概念 2. 事务的ACID原则 3. MySQL实现事务的方法 4. MySQL实现事务的步骤 5. 事务的原子性、一致性、持久性 6. 事务的隔离性 7. MySql中的锁 1. 共享锁 2. 排他锁 3. 行级锁 4. 表级锁 5. 间隙锁 6. 临键锁 7. 记录锁 8. 意向共享锁…

1.spring入门案例

Spring 介绍 Spring是轻量级的开源的JavaEE框架。 Spring有两个核心部分&#xff1a;IOC和AOP IOC 控制反转&#xff0c;把创建对象过程交给Spring进行管理。 AOP 面向切面&#xff0c;不修改源代码进行功能增强。 Spring特点 1.方便解耦&#xff0c;简化开发。 2.AOP编…

docker安装sqlserver2019

1、背景 由于要学习flink cdc&#xff0c;并且数据源是sqlserver&#xff0c;所以这里采用docker安装sqlserver。 2、安装步骤 &#xff08;1&#xff09;建目录 // 创建指定的目录 mkdir sqlserver// 进入该目录 cd sqlserver// 创建/data/mssql目录 mkdir -p /data/mssql…

SpringBoot + mkcert ,解决本地及局域网(内网)HTTPS访问

本文主要解决访问SpringBoot开发的Web程序,本地及内网系统,需要HTTPS证书的问题。 我测试的版本是,其他版本不确定是否也正常,测试过没问题的小伙伴,可以在评论区将测试过的版本号留下,方便他人参考: <spring-boot.version>2.3.12.RELEASE</spring-boot.vers…

Ubuntu20.04安装vimplus插件

参考文章&#xff1a; Ubuntu Linux下vimplus的安装及使用安装vimplus之后乱码问题解决 1、安装步骤&#xff1a; $ git clone https://github.com/chxuan/vimplus.git ~/.vimplus$ cd ~/.vimplus$ ./install.sh2、./install.sh 过程 出现选择是否备份 /home/yin-roc/.vim…

注意力机制之ECA-Net:Efficient Channel Attention for Deep Convolutional Neural Network

论文link&#xff1a;link code&#xff1a;code 1.摘要 近年来&#xff0c;通道注意机制被证明在改善深层卷积神经网络&#xff08;CNN&#xff09;的性能方面提供了巨大的潜力。然而现有的大多数方法都致力于开发更复杂的注意模块以获得更好的性能&#xff0c;这不可避免地增…

博客都在使用的打字机效果,居然这么简单?

效果展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>body …

历年各省废水污染、治理数据

1996-2019年各省废水污染、治理数据https://download.csdn.net/download/a519573917/89485042 目录 摘要&#xff1a; 一、引言 二、文献综述 三、实证模型 &#xff08;一&#xff09;变量选择 &#xff08;二&#xff09;数据来源 &#xff08;三&#xff09;模型设定…

timm中模型更换huggingface模型链接

现在timm默认使用huggingface的链接了&#xff0c;错误链接如下&#xff1a; (MaxRetryError("HTTPSConnectionPool(hosthuggingface.co, port443): Max retries exceeded with url: /timm/swinv2_tiny_window8_256.ms_in1k/resolve/main/model.safetensors (Caused by C…