队列------数据结构

news2024/11/18 23:32:09

队列:Queue是一个普通的队列,Deque是一个双端队列

普通的队列:只能是队尾进,队头出;

双端队列:可以从队头进队尾出,也可以从队尾进,队头出,也可以用作一个栈;

1)下面我们来介绍一下Queue的实现方法:

在有容量限制的情况下,add方法可能会抛出异常,但是offer方法不会抛出异常

offer()  poll()  peek() 容量满了都不会抛出异常
add()   remove()  element()  容量满了会抛出异常

1)add(E)

2)remove(E)

3)poll()

4)remove()

5)element()

6)peek()

2)下面我们来介绍一下Deque的常用方法:双端队列也是可以当作栈使用

2.1)删除双端队列中的队首元素

removeFirst()   removeList()  pollFirst()  pollLast()

2.2)获取双端队列中的队首元素


getFirst()  getLast() peekLast()  peekFirst()

2.3)新增元素


add() addFirst() addLast()

offer() offerFirst()  offerLast()


addFirst()和addLast()底层默认是调用LinkFirst(元素)和LinkLast(元素)

 Queue queue=new LinkedList();
      //1放元素
       queue.add(1);   offer()
       queue.add(2);
       //2获取队头元素
       System.out.println(queue.peek());
       System.out.println(queue.element());
       //3出队列的元素
       System.out.println(queue.poll());
       System.out.println(queue.remove());
  Deque<Integer> queue=new LinkedList<>();
       //queue.add(1);//默认是从队尾进入的   offerFirst
       queue.addFirst(1);//默认是队头入队的
       queue.addLast(2);//默认是从队头入队的  offerLast
       queue.addFirst(3);
       queue.addLast(4);
       System.out.println(queue.peekFirst()); 
       System.out.println(queue.peekLast());   
       //上面打印的结果是3,4

1)对于LinkedList,它不仅可以作为普通的队列,还可以作为双端队列,还可以作为双向链表(里面有一个头指针和一个尾指针),还可以作为栈来进行使用,还有一个尴尬的方法

add(int index,E element)

2)ArrayList是一个顺序存储,物理内存是连续的,逻辑上也是连续的,但是LinkedList在物理内存上面是不连续的,但是逻辑上是连续的

3)说区别一定要从从增删改查和内存存储来说,新增有什么区别?删除有什么区别?修改有什么元素?查找有什么区别?

下面用一个单链表来实现一个队列

对于队列来说,如果说我们是使用数组来进行实现队列,那么出队列是从数组头上面出数据,那么效率会变得非常低

4)LinkedList实现了list接口,Deque接口,LinkedList重写了Deque的方法和Queue的方法

 Queue<Integer> queue=new LinkedList<>();//这是把LinkedList当做一个普通的队列来进行使用
 Deque<Integer> deque=new LinkedList<>();//这是把LinkedList当做一个双端队列来进行使用

那么现在问题就来了?如果是单链表,哪边是队头,那边是队尾呢?

1)head是队头,tail是队尾按照尾插法,入队操作的时间复杂度是O(N)(每次都要找尾巴节点,从尾巴节点进行插入),出队的时间复杂度是O(1),那么这时只出队头元素就行了;

2)如果Head是队尾,tail是队头,头插法入队的时间复杂度是O(1),找尾巴出队的时间复杂度就是O(N)

3)如果说我们的head是队头,我们用一个last指针指向队尾节点,每一次使用尾插法入队,那么时间复杂度就是O(1),出队列的时间复杂度就是O(1)

4)我们再进行新增节点的时候,分两种情况,第一次进行入队列的操作,和不是第一次入队列的操作;

在进行出队列的时候,我们还要进行判断一下当前队列是不是空;

自己实现一个队列 

结论:head是队头,last是队尾,此时入队的时间和出队的时间复杂度就是O(1)

出队:head=head.next

入队:tail.next=node,tail=tail.next

时间复杂度:新增元素,插入元素时间复杂度都是O(1)
class Node{
   public int data;
   public Node next;

    public Node(int data) {
        this.data = data;
    }
}
class MyQueue{
public Node head;
public Node tail;
public void offer(int data)
{  //创建一个新的节点
    Node node=new Node(data);
    if(head==null)
    {
      head=node;
      tail=node;
    }else{
       tail.next=node;
       tail=tail.next;
    }
}
public boolean isEmpty()
{
    return this.head==null;
}
public int pop()
{   //1判断对头是否为空
    if(isEmpty())
    {
        throw  new RuntimeException("此时队列时空");
    }
    //2只需队头出去即可
    int data= head.data;
    head=head.next;
    return data;
}

public int peek()
{
    return head.data;
}
}
泛型+链表
class Node<T>{
    public T data;
    public Node next;
    public Node(T data)
    {
        this.data=data;
    }
}
class MyQueue<T>{
    public Node head=null;
    public Node tail=null;
    public int count=0;
    public boolean IsEmpty()
    {
        return count==0;
    }
    public void offer(T t)
    {
        Node node=new Node(t);
        if(head==null)
        {
            head=node;
            tail=node;
            count++;
            return;
        }else{
            tail.next=node;
            tail=tail.next;
            count++;
        }
    }
    public T peek()
    {
        if(IsEmpty())
        {
            throw new RuntimeException("当前队列的元素是空");
        }
        return (T)head.data;
    }
    public T poll()
    {
        if(IsEmpty())
        {
            throw new RuntimeException("当前队列的元素是空");
        }
        T data= (T) head.data;
        head=head.next;
        count--;
        return data;
    }
    public int size()
    {
        return count;
    }

}

public class HelloWorld{
    public static void main(String[] args) {
     MyQueue<String> queue=new MyQueue<>();
     queue.offer("生命在于运动");
     queue.offer("生命在于吃饭");
     queue.offer("生命在于潇洒");
     System.out.println(queue.size());//3
     String str1= queue.peek();//生命在于运动
        System.out.println(str1);
        String str2=queue.poll();//生命在于运动
        System.out.println(str2);
        System.out.println(queue.size());//2
     String str3= queue.poll();//生命在于吃饭
        System.out.println(str3);
        String str4= queue.peek();//生命在于潇洒
        System.out.println(str4);
        String str5=queue.poll();//生命在于潇洒
        System.out.println(str5);
        String str6=queue.poll();//抛出异常
    }
}

 3.设计循环队列

能不能拿数组写一个队列呢?

可以,数组要写成循环,这样才可以让数组的空间利用率达到最高

1)当新增元素的时候,让tail++,count++如果发现tail已经加到了数组的末尾,就将tail=0;

2)出循环队列的时候,让head++,count--如果此时发现了head已经减到了数组的末尾,就将head=0;

在这里面我们要注意一些点:当我们设置循环队列的时候,head==tail的时候,有两种情况

1)还没有进行放元素的时候

2)当整个循环队列的内容就已经满了

这里面返回队尾元素的时候:

1)tail永远是比咱们的放的元素位置+1,返回队首元素的时候直接返回array[tail-1]

2)如果tail的下标变成0了,那么直接返回array[array.length-1]

class MyCircularQueue {
  private int[] arr1;
    int count=0;
    int head=0;
    int tail=0;
    public MyCircularQueue(int k) {
         this.arr1=new int[k];
    }
    
    public boolean enQueue(int value) {
        if(isFull())
        {
           return false;
        }
        arr1[tail]=value;
        tail++;
        count++;
        if(tail==arr1.length)
        {
            tail=0;
        }
         return true;
    }
    
    public boolean deQueue() {
        if(isEmpty())
        {
            return false;
        }
        head++;
        if(head==arr1.length)
        {
            head=0;
        }
        count--;
        return true;
    }
    
    public int Front() {
        if(isEmpty())
        {
            return -1;
        }
   return arr1[head];
    }
    
    public int Rear() {
        if(count==0)
        {
            return -1;
        }
        if(tail==0) return arr1[arr1.length-1];
        return arr1[tail-1];
    }
    public boolean isEmpty() {
    return count==0;
      }
    public boolean isFull() {
        return count==arr1.length;
    }
}

拓展:当我们的tail下角标和head的下角标相等的时候,用数组实现的循环队列中的元素可能是空,也有可能是满的,那么当tail==head的时候,我们该如何进行判断里面的元素是否为空,或者是否满了呢?

1)第一种方法,我们用count来记录数据的个数

2)我们使用标志位的方式来进行判断,标识位初始值是false

if(tail==head&&flag==false)表示队列此时为空

if(tail!=head&&flag==false)表示队列没有满

if(tail==head&&flag==true)表示队列此时为满

每当我们新增元素,我们就将flag置为true,如果说我们每一次删除元素,我们就将flag置为false

class MyCircularQueue {
   public int head=0;
   public int tail=0;
   boolean flag;
   public int[] array;
    public MyCircularQueue(int k) {
       this.array=new int[k];
       this.flag=false;
    } 
    public boolean enQueue(int value) {
        if(isFull()){
            return false;
        }
        array[tail]=value;
        flag=true;
        tail++;
        if(tail==array.length){
            tail=0;
        }
        return true;
    }  
    public boolean deQueue() {
         if(isEmpty()){
             return false;
         }
         head++;
         flag=false;
         if(head==array.length){
             head=0;
         }
         return true;
    } 
    public int Front() {
        if(isEmpty()) return -1;
        return array[head];
    }
    public int Rear() {
        if(isEmpty()) return -1;
        //这里面的条件不可以写成if(isFull())
        if(tail==0) return array[array.length-1];
        return array[tail-1];
    }
    public boolean isEmpty() {
          return head==tail&&flag==false;
    }   
    public boolean isFull() {
           return head==tail&&flag==true;
    }
}

3)我们进行浪费一块空间tail永远指向空格子

每一次我们存放元素之前,我们都来进行检查一下看看tail的下一个是不是head,如果是那么说明就是满的,那么我们就不存放元素了

if((tail+1)%array.length==head)表示此时循环队列的元素已经满了

if(tail==head)说明此时队列为空

class MyCircularQueue {
    public int head=0;//队头下标
    public int tail=0;//队尾下标
    public int[] array;
    public MyCircularQueue(int k) {
        this.array=new int[k+1];
    }
    public boolean enQueue(int value) {
           if(isFull()) return false;
           array[tail]=value;
           tail++;
           if(tail==array.length){
               tail=0;
           }
//tail=(tail+1)%array.length;
           return true;
    } 
    public boolean deQueue() {
        if(isEmpty()){
            return false;
        }
        head++;
        if(head==array.length)
        head=0;
//head=(head+1)%array.length
        return true;
    } 
    public int Front() {
         if(isEmpty()){
             return -1;
         }
         return this.array[head];
    }    
    public int Rear() {
        if(isEmpty()){
            return -1;
        }
       if(tail==0){
           return array[array.length-1];
       }
       return array[tail-1];
    }    
    public boolean isEmpty() {
         return this.head==this.tail;
    }
    public boolean isFull() {
            return (this.tail+1)%array.length==head;
    }
}

 

练习题1:用队列来实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)

实现 MyStack 类:

void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

题解:栈先进后出的数据结构,队列是先进先出的数据结构

刚一开始进栈的时候,两个队列都是空的,所以先进哪一个都可以,我们此时就约定先进第1个吧;

1)每次入栈的时候都入到不为空的队列

2)出栈,一个不为空队列的中的所有元素-1都出队列,放到另一个为空队列B里面,然后最后的出栈操作,剩下的元素就是出栈的元素

3)写OJ题的时候,Queue<Integer> queue=new LinkedList<>();

你进行获取栈顶元素的时候不能影响到(进行操作队列的时候,不能影响到原来的顺序,保存的元素一定是完整的)


class MyStack{
    private Queue<Integer> queue1=null;
    private Queue<Integer> queue2=null;
    public MyStack()
    {
        this.queue1=new LinkedList<>();
        this.queue2=new LinkedList<>();
    }
    public void push(int data)
    {
        if(!queue1.isEmpty())
        {
            queue1.add(data);
        }else if(!queue2.isEmpty())
        {
            queue2.add(data);
        }else{
            //这种情况下两个队列都是空队列,我们默认放到第一个队列当中
            queue1.add(data);
        }
    }
    public int pop()
    {
        if(!queue1.isEmpty())
        {
            int size=queue1.size();
            for(int i=0;i<size-1;i++)
            {
                int num=queue1.poll();
                queue2.add(num);
            }
            return queue1.poll();
        }else{
            int size= queue2.size();
            for(int i=0;i<size-1;i++)
            {
                int num= queue2.size();
                queue1.add(num);
            }
            return queue2.poll();
        }
    }
 
}

 练习题2:用栈来实现队列

题解:我们使用两个栈来进行实现一个队列

我们入队列指定到入到S1这个栈

我们出队列指定到出S2这个栈,我们如果发现S2这个栈是空的,那么就把S1中的队列中的元素全部放到S2中,再进行出栈顶元素

class MyQueue {
    public Stack<Integer> stack1=null;
    public Stack<Integer> stack2=null;
    public MyQueue() {
           this.stack1=new Stack<>();
           this.stack2=new Stack<>();
    }
    public void push(int x) {
         stack1.push(x);
    }
    
    public int pop() {
        if(!stack2.isEmpty())
        {
            int data=stack2.pop();
            return data;
        }else{
            int count=stack1.size();
            for(int i=0;i<count-1;i++)
            {
                int data=stack1.pop();
                stack2.add(data);
            }
            return stack1.pop();
        }

    }
    
    public int peek() {
         if(!stack2.isEmpty())
        {
            int data=stack2.peek();
            return data;
        }else{
            int count=stack1.size();
            for(int i=0;i<count-1;i++)
            {
                int data=stack1.pop();
                stack2.add(data);
            }
            int str=stack1.pop();
            stack2.push(str);
            return str;     
        }
    }
    public boolean empty(){
    return stack1.isEmpty()&&stack2.isEmpty();
    }
}

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

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

相关文章

你是真的“C”——详解函数递归

详解函数递归运用&#x1f60e;前言&#x1f64c;一、什么是递归&#x1f64c;二、递归运用的两个必要条件&#x1f64c;三、递归与迭代&#x1f64c;总结撒花&#x1f49e;哈喽&#xff01;&#x1f604;各位CSDN的uu们&#xff0c;我是你的博客好友小梦&#xff0c;希望我的文…

长安链 VM Engine架构设计深度解读

VM Engine是长安链智能合约引擎的推荐选型&#xff0c;采用Docker容器化架构&#xff0c;容器内部由一个任务调度器和多个合约进程组成&#xff0c;实现了多合约隔离与多进程并发&#xff0c;支持独立部署&#xff0c;目前支持Golang语言合约。 1. 背景说明 自2009年11月以来&…

CUDA 冬令营1

基本概念 1.CPU的任务&#xff1a;为串行任务优化 2.GPU的任务&#xff1a;为并行任务优化 3.L4T ubuntu&#xff1a;基于NVIDIA Tegra芯片的ubuntu系统&#xff08;linux for Tegra&#xff09; 4.Tegra&#xff1a;继承了ARM CPU 和 NVIDIA GPU的处理器芯片 5.X86&#xff1…

2023京东年货节全民炸年兽活动最详细规则

2023京东全民炸年兽活动规则 1、活动时间 整体活动时间: 2022年12月26日00: 00: 00–2023年01月15日23: 59: 59 2、活动玩法 (1)玩法一:全民炸年兽瓜分10亿压岁钱 活动时间: 2022年12月26日00: 00: 00–2023年01月15日23: 59: 59 (2) 玩法二:每晚8点分百万红包 活动时间…

Houdini和C4D区别在哪?哪个更好用

Houdini和C4D作为当前软件市场上非常热门的设计软件&#xff0c;现在越来越多的小伙伴开始学习。所以咱们今天就从行业应用、建模、动画和使用难易度等进行多方面对比&#xff0c;帮助小伙伴们更清楚地了解这两款软件—— Houdini作为一款非常受欢迎的3D&VFX制作工具&#…

美国公司是如何搞创新的,又是如何失败的......

PARC&#xff0c;施乐帕洛阿图研究中心&#xff0c;和贝尔实验室媲美的IT界圣地。从这里走出了一大批伟大的发明&#xff0c;直接改变了整个计算机行业。Alto PC &#xff0c;图形用户界面&#xff0c;所见即所得&#xff0c;以太网&#xff0c;PostScript&#xff0c;面向对象…

复购高,退货低的日本市场成为跨境电商新风口,新手如何快速入局

据统计预测&#xff0c;未来几年将会有越来越多的跨境卖家涌入日本电商市场。但由于在语言、文化和消费习惯上存在一定的差异&#xff0c;很多中国卖家并不熟悉日本的文化与市场需求&#xff0c;也很难在短时间内快速适应日本电商平台的运营规则与服务。 日本作为一个经济发达的…

【PS-选区编辑】变换选区、反向命令、建立工作路径

目录 变换选区 1、位置 2、多种操作 反向命令 1、反选选区&#xff1a;ctrlshifti 2、边界 3、平滑 4、扩展和收缩 5、羽化 建立工作路径 变换选区 1、位置 制作了一个制作了选区后&#xff0c;鼠标右击找到【变换选区】&#xff0c;或在【选择】菜单中找到【变换…

JavaScript 入门基础 - 运算符(三)

JavaScript 入门基础 - 运算符&#xff08;三&#xff09; 文章目录JavaScript 入门基础 - 运算符&#xff08;三&#xff09;1.什么是运算符2.表达式和返回值3.算术运算符概述4. 赋值运算符5.递增和递减运算符5.1 递增和递减运算符概述5.2 递增运算符5.2.1 前置递增运算符5.2.…

Ajax(1)---了解Ajax和XML

目录 Ajax XML了解 Ajax的特点 HTTP协议请求报文与响应文本结构 HTTP 请求报文 响应报文 Ajax AJAX全称为Asynchronous JavaScript And XML&#xff0c;就是异步的JS和XML通过AJAX可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势:无刷新获取数据。AJAX不是新…

一篇带你彻底弄懂SpringBoot项目jdk版本及依赖不兼容问题

&#x1f49f;&#x1f49f;前言 ​ 友友们大家好&#xff0c;我是你们的小王同学&#x1f617;&#x1f617; 今天给大家打来的是 一篇带你彻底弄懂SpringBoot项目jdk版本及依赖不兼容问题 希望能给大家带来有用的知识 觉得小王写的不错的话麻烦动动小手 点赞&#x1f44d; 收…

MySQL 大表优化方案

单表优化 读写分离 缓存 表分区 垂直拆分 水平拆分 兼容MySQL且可水平扩展的数据库 NoSQL 当MySQL单表记录数过大时&#xff0c;增删改查性能都会急剧下降&#xff0c;可以参考以下步骤来优化&#xff1a; 单表优化 除非单表数据未来会一直不断上涨&#xff0c;否则不…

通过Django发送邮件

通过Django发送邮件非常的简单&#xff0c;在Python中已经内置了一个smtplib邮件发送模块&#xff0c;Django在此基础上进行了简单地封装&#xff0c;我们可以在Django的环境中方便的发送邮件。大部分邮件发送成功主要是邮件的参数配置&#xff0c;本文以常用的126邮箱和QQ邮箱…

《2022中国数据智能产业图谱3.0版》重磅发布

‍数据猿出品本次“数据猿行业盘点季大型主题策划活动——《2022中国数据智能产业图谱3.0版》”为2022年度图谱版本的升级更新版&#xff0c;下一次版本迭代将于2023年4月底发布2023年1.0版&#xff0c;敬请期待&#xff0c;欢迎报名。‍数据智能产业创新服务媒体——聚焦数智 …

svn修改提交日志信息

参考&#xff1a;唐小码个人博客 一、svn修改提交的msg信息和作者信息 鼠标右键找到show log> 选择要修改的日志行&#xff0c;第一个是修改作者信息&#xff0c;第二个是修改日志信息 二、svn修改提交的日期信息 修改日期信息的话&#xff0c;你得先有svn服务器的权限&…

光电接收的TIA设计

APD的光电接收电路如下图所示&#xff0c;APD的光电流较SiPM是小很多的&#xff0c;所以需要TIA来做跨阻放大&#xff0c;一般RF电阻要5k到10k左右&#xff0c;光电流一般也就几十uA&#xff0c;这个跨阻放大主要是对Cf大小的选择&#xff0c;Cf的选择主要考虑下面的两点。从放…

【基础强训】day4

一、选择题 &#x1f308;第1题 有以下程序 #include<iostream> #include<cstdio> using namespace std; int main(){ int m0123, n123; printf("%o %o\n", m, n); return 0; } 程序运行后的输出结果是&#xff08;&#xff09; A 0123 0173 B 01…

汇聚数据库创新力量,加速企业数字化转型

2022年12月29日&#xff0c;以“汇聚数据库创新力量&#xff0c;加速企业数字化转型”为主题的openGauss Summit 2022在线上举行。会上&#xff0c;openGauss社区理事会理事长江大勇对外公布了最近社区及生态进展&#xff0c;并宣布推出资源池化架构&#xff0c;实现软硬融合&a…

人脸识别实战教程

人脸识别实战教程 本教程介绍如何使用Alibaba Cloud SDK for Java进行人体属性识别、人体计数和人脸搜索任务。 背景信息 人脸人体识别技术是基于阿里云深度学习算法&#xff0c;结合图像或视频的人脸检测、分析、比对以及人体检测等技术&#xff0c;为您提供人脸人体的检测…

【Java语言】— 运算符、案例:键盘录入

目录运算符1.算术运算符案例&#xff1a;数值拆分2.“”做连接符3.自增自减运算符自增自减的使用事项自增自减的案列4.赋值运算符基本赋值运算符&#xff1a;“”扩展赋值运算符&#xff1a;“”、“-”、“*”、“/”5.关系运算符6.逻辑运算符短路逻辑运算符总结7.三元运算符三…