IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 数据结构---还不懂栈和队列,来看这篇收获满满 -> 正文阅读

[数据结构与算法]数据结构---还不懂栈和队列,来看这篇收获满满

目录

一:栈(Stack)

1.1:概念

?1.2:栈的使用

1.3:栈的应用场景

1.3.1:改变元素序列

1.3.2:将递归转化为循环

1.4:栈的模拟实现

1.5:栈、虚拟机栈、栈帧有什么区别呢?

二:队列(Queue)

2.1:概念

2.2:队列的使用

循环队列

力扣题:设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

2.3:队列模拟实现

三:双端队列(Deque)了解


一:栈(Stack)


1.1:概念

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

就比如给枪上子弹,你先上的已经被后上的挤入弹夹深处了,先打出的子弹一定是后上的子弹。

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

出栈:栈的删除操作叫做出栈。出数据在栈顶。

注意:

  • 栈只能在栈顶进行插入和删除以及元素的访问操作
  • 不能在任意位置进行插入和删除以及元素访问
  • 也不能遍历

?1.2:栈的使用

Stack继承了Vector中的一些方法

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

1.3:栈的应用场景

1.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

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

A: 12345ABCDE B: EDCBA54321 C: ABCDE12345 D: 54321EDCBA

1.3.2:将递归转化为循环

为什么可以使用栈将递归转换为循环而不是其他结构?还记得之前的单链表的逆序打印吗?

!!!敲重点:如何将单链表逆序打印?_myi__的博客-CSDN博客

我们发现:后调用的先退出,先调用的后退出----->方法之间的递归调用次序刚好和栈的特性是匹配的。

public void printReverse(Node<E> head){
   if(head == null){
         return;
   }
//一次将链表中的元素保存到栈中
   Stack<E> s = new Stack<>();
   Node<E> cur = head;
   while(cur!=null){
        s.push(cur.value);
        cur = cur.next;
   }
//将栈中的元素依次删除掉
   while(!s.empty()){
        System.out.print(s.pop()+" ");
   }
   System.out.println();
}

注意:并不是所有的递归都得用到栈。

1.4:栈的模拟实现

Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的。

类和接口总览:??????https://blog.csdn.net/myi__/article/details/121387084

import java.util.Arrays;

public class Stack<E> {
    E[] array;
    int size; //用来表示这个栈中有多少个元素,也可以表示栈顶元素的位置
    public Stack(){
        array = (E[]) new Object[3];
        size = 0;
    }
    public void push(E e){
        //1.先确保栈中空间足够
        ensureCapacity();
        //2.插入元素
        array[size] = e;
        size++;
    }
    public E pop(){
        E ret = peek();
        size--;
        return ret;
    }
    public E peek(){
        if(empty()){
            throw new RuntimeException("pop:栈是空的");
        }
        return array[size-1];
    }
    public boolean empty(){
        return 0 == size;
    }
    public int size(){
        return size;
    }
    private void ensureCapacity(){
        if(size == array.length){
            int newCapacity = size*2;
            array = Arrays.copyOf(array,newCapacity);
        }
    }

    public static void main(String[] args) {
        Stack<String> s = new Stack<>();
        s.push("111");
        s.push("222");
        s.push("333");
        s.push("444");
        s.push("555");
        s.push("666");
        System.out.println(s.size); //6
        System.out.println(s.peek()); //666

        s.pop();
        s.pop();
        System.out.println(s.size); //4
        System.out.println(s.peek()); //444

    }
}

1.5:栈、虚拟机栈、栈帧有什么区别呢?

一般认为时后进先出的一种数据结构---在java的集合中有对应的实现---Stack,Stack在实现时继承了Vector。

虚拟机栈具有特殊作用的一块内存空间。

? ? ? ? ? ? ? ? ? jvm为了对数据进行更好的管理,将内存按照不同的需求划分出来的一种结构

? ? ? ? ? ? ? ? ? 栈区:线程私有的,栈区中存放的是函数调用相关的一些信息,主要是栈帧

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?当栈区中内存空间不足时,会抛出StackOverflowException

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?当中的元素(栈帧)是按照数据结构中的栈的特性来实现的

栈帧一种结构,这种结构与函数调用相关的,内部:局部变量表、操作数栈......

? ? ? ? ? ?每个方法在运行时,jvm都会创建一个栈帧,然后将栈帧压入到虚拟机栈中

? ? ? ? ? ?当方法调用结束时,该方法对应的栈帧会从虚拟机栈中出栈

? ? ? ? ? ?注意:每个方法的栈帧中结构都是一样,大小可能不同,栈帧的大小在程序编译时已? ? ? ? ? ? ?经确定好的

二:队列(Queue)


2.1:概念

只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表

队列具有先进先出FIFO(First In First Out)

入队列:进行插入操作的一端称为队尾(Tail/Rear)

出队列:进行删除操作的一端称为队头 (Head/Front)

2.2:队列的使用

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

方法功能
boolean offer(E e)入队列
E poll()出队列
peek()获取队头元素
int size()获取队列中有效元素个数
boolean isEmpty()检测队列是否为空
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);
        q.offer(6);
        System.out.println(q.size()); //6
        System.out.println(q.peek()); //查看队头元素---1

        q.poll();//删除队头元素
        System.out.println(q.size());//5
        System.out.println(q.peek()); // 2

每个方法的时间复杂度都是O(1)

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

提问:队列底层是使用LinkedList实现的,为什么不使用连续的空间呢?会造成假溢出

如果使用连续空间,也可以达到时间复杂度都是O(1)的需求

虽然方式二也能达到链表的效果,但是它也是有缺陷的。如果你想存储有效元素但是空间存满的话,就算队头的元素已经出队列了,位置空出来了,rear也已经走到空间末尾了,可想而知元素就插不进去了,但其实有效元素并未存满。上述现象就是队列的假溢出。

真溢出是队列中有效元素已经和空间大小相等了,再放不进去元素了。

想要解决假溢出的问题,可以通过循环队列来解决

循环队列

力扣题:设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。

class MyCircularQueue {
    int[] array;
    int front = 0;   //队头
    int rear = 0;    //队尾
    int count = 0;   //记录队列里有效元素的个数
    int N;
    public MyCircularQueue(int k) {
    array = new int[k];
    N = k;
    }
    
    public boolean enQueue(int value) {
        if(isFull()){
            return false;
        }

        //直接往rear位置插入元素
        array[rear] = value;
        rear++;
        if(rear == N){
            rear = 0;
        }
        count += 1;
        return true;
    }
    
    public boolean deQueue() {
        if(isEmpty()){
            return false;
        }
        front++;
        front %= N;
        count -= 1;
        return true;
    }
    
    public int Front() {
        if(isEmpty()){
            return -1;
        }
        return array[front];
    }
    
    public int Rear() {
         if(isEmpty()){
            return -1;
        }
        return array[(rear-1+N)%N];
    }
    
    public boolean isEmpty() {
        return 0 == count;
    }
    
    public boolean isFull() {
        return count == array.length;

    }
}

2.3:队列模拟实现

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

//模拟实现队列 --- 底层使用双向链表 --- 在集合框架Queue是一个接口 --- 底层使用的是LinkedList
public class Queue<E> {
    //双向链表结点
    public static class ListNode<E>{
        ListNode<E> next;
        ListNode<E> prev;
        E value;

        ListNode(E value){
            this.value = value;
        }
    }
    ListNode<E> first;  //队头
    ListNode<E> last;   //队尾
    int size;

    //入队列---向双向链表尾部插入新节点
    public void offer(E e){
       ListNode<E> newNode = new ListNode<>(e);
       if(first == null){
           first = newNode;
       }else{
           last.next = newNode;
           newNode.prev = last;
       }
       last = newNode;
       size++;
    }

    //出队列---将双向链表首结点删除掉
    public E poll(){
        //1.队列为空
        //2.队列中只有一个元素
        //3.队列中有多个元素
        E value = null;
        if(first == null){
            return null;
        }else if(first == last){
            first = null;
            last = null;
        }else {
            value = first.value;
            first = first.next;
            first.prev.next = null;
            first.prev = null;
        }
        size--;
        return value;
    }
    //获取队头元素
    public E peek(){
        if(first == null){
            return null;
        }
        return first.value;
    }
    public int size(){
        return size;
    }
    public boolean isEmpty(){
        return first == null;
    }

    public static void main(String[] args) {
        Queue<Integer> q = new Queue<>();
        q.offer(1);
        q.offer(2);
        q.offer(3);
        System.out.println(q.size()); //3
        System.out.println(q.peek()); //1
        System.out.println(q.isEmpty()); //false

        q.poll();
        q.poll();
        q.poll();
        System.out.println(q.size()); //0
        System.out.println(q.peek()); //null
        System.out.println(q.isEmpty()); //true
    }
}

三:双端队列(Deque)了解


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

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

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-11-22 12:35:49  更:2021-11-22 12:36:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/9 16:15:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码