队列------数据结构
创始人
2024-05-03 01:00:17
0

队列: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 queue=new LinkedList<>();//queue.add(1);//默认是从队尾进入的   offerFirstqueue.addFirst(1);//默认是队头入队的queue.addLast(2);//默认是从队头入队的  offerLastqueue.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 queue=new LinkedList<>();//这是把LinkedList当做一个普通的队列来进行使用Deque 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{public T data;public Node next;public Node(T data){this.data=data;}
}
class MyQueue{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 queue=new MyQueue<>();queue.offer("生命在于运动");queue.offer("生命在于吃饭");queue.offer("生命在于潇洒");System.out.println(queue.size());//3String str1= queue.peek();//生命在于运动System.out.println(str1);String str2=queue.poll();//生命在于运动System.out.println(str2);System.out.println(queue.size());//2String 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.lengthreturn 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 queue=new LinkedList<>();

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


class MyStack{private Queue queue1=null;private Queue 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

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

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

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

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

class MyQueue {public Stack stack1=null;public Stack 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

相关内容

热门资讯

【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...