【数据结构与算法】队列-模拟实现队列以及设计循环队列

简介: 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列是一种先进先出的数据结构,注意和栈进行区分,不要记混.

队列的概念


队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

队列是一种先进先出的数据结构,注意和栈进行区分,不要记混.

队列的实现有链式结构和顺序结构,接下来会使用链表和数组分别实现队列

队列中的方法有以下这些:

方法 描述

offer(E e) 入队列

poll() 出队列

peek() 获取队头元素

int size() 获取队列中有效元素个数

boolean isEmpty() 检测队列是否为空

接下来来实现这些功能


链表实现栈


链表实现栈有以下点需要注意:

入队只能在队尾增加元素,可以遍历到最后一个元素,再进行增加元素 但每次入队都要进行遍历,就会很慢,为了解决这个问题,可以用tail记录下来最后一个元素,这样入队时,直接用tail就可以了

在出队时要注意空指针异常,也就是当前队列为空的情况 我这里解决的方法是使用自定义异常也可以使用别的方法来解决

除了空队列的这种情况,还要考虑只有一个元素的情况,如果入队的元素是第一个,要将head和tail同时指向要入队的结点. 如果当前队列只有一个元素,进行出队时,要将head和tail都置为null

代码如下:

public class MyQueue extends MyNullException{

   static class Node{

       public int val;

       public Node next;

       public Node(int val) {

           this.val = val;

       }

   }

   public Node head;

   public Node tail;

   /**

    * 入队

    * add与offer的区别

    * @param val

    */

   public void offer(int val){

       Node node = new Node(val);

       if (isEmpty()){

           head = node;

           tail = node;

       }else {

           tail.next = node;

           tail = tail.next;

       }

   }

   /**

    * 出队

    * @return

    */

   public int poll(){

       if (isEmpty()){

           throw new MyNullException("当前队列为空,出队失败!");

       }

       int ret = head.val;

       if (head.next == null){

           head = null;

           tail = null;

       }else {

           head = head.next;

       }

       return ret;

   }

   /**

    * 查看队头元素

    * @return

    */

   public int peek(){

       if (isEmpty()){

           throw new MyNullException("当前队列为空,查看失败!");

       }

       return head.val;

   }

   public boolean isEmpty(){

       return head == null;

   }

   public int size(){

       int ret = 0;

       Node cur = head;

       while (cur != null){

           ret++;

           cur = cur.next;

       }

       return ret;

   }

}

public class MyNullException extends RuntimeException{

   public MyNullException(){

   }

   public MyNullException(String message){

       super(message);

   }

}


设计循环队列


接下来用数组来实现队列,用数组来实现队列注意是采用循环队列这种方式

111111.png

一般情况下,这样实现队列会有什么问题,首先队列出队是从队头出元素,队尾新增元素,出队时只需要front往前走一步,入队只需要让数组的rear下标设置为要增加元素的值,再让rear往后走一步就行,但是数组的长度是有限的,如果当前数组满了,就需要进行扩容,但之前如果频繁出队会造成数组的前面空间大量的浪费.

因此为了解决这个问题,推荐使用数组设计一个循环队列,循环队列最大的特点就是 front 可以不从0下标开始放数据,而rear也可以在队列不满的情况下,即使到了数组的最后一个元素,可以接着从0下标开始存放数据

看下图:

1111111.png

上图可知:front是从1下标开始存放元素的,如果此时rear下标是9,在入队一个元素,如何让9到0下标呢?此时就不能只让rear自增1了,而要让rear+1后进行取余

入队时: rear = (rear+1) % 数组长度;

出队时: front = (front+1) % 数组长度;

那么接下来要考虑一个问题,什么时候这个队列是满的?front等于rear?

front等于rear也可能是空队列,为了解决这个问题,有两种方法:

使用计数器,将数组中元素的有效个数记录下来

浪费一个空间,也就是当 (rear+1) % 数组长度== front 时为队列满这种情况

代码如下:

public class MyCircularQueue{

   public int[] elem;

   public int usedSize;// 有效个数

   public int front;

   public int rear;

   public MyCircularQueue(int k){

       this.elem = new int[k+1];

   }

   /**

    * 入队

    * 如果需要扩容,就需要考虑从哪里开始拷贝

    * @param val

    * @return

    */

   public boolean enQueue(int val){

       if (!isFull()){

           elem[rear] = val;

           rear = (rear+1) % elem.length;

           // usedSize++;

           return true;

       }else{

           return false;

       }

   }

   /**

    * 出队

    * @return

    */

   public boolean deQueue(){

       if (isEmpty()){

           return false;

       }else {

           front = (front+1) % elem.length;

           // usedSize--;

           return true;

       }

   }

   /**

    * 获取队头元素

    * @return

    */

   public int Front(){

       if (isEmpty()){

           return -1;

       }else {

           return elem[front];

       }

   }

   /**

    * 获取队尾元素

    * 注意下标: 0和elem.length的时候

    * @return

    */

   public int Rear(){

       if (isEmpty()){

           return -1;

       }

       return (rear == 0) ? elem[elem.length-1] : elem[rear-1];

   }

   /**

    * 判断当前队列是否满了

    * @return

    */

   public boolean isFull(){

       /*if (usedSize == elem.length){

           return true;

       }

       return false;*/

       return (rear+1) % elem.length == front;

   }

   /**

    * 判断队列是否为空

    * @return

    */

   public boolean isEmpty(){

       /*if (usedSize == 0){

           return true;

       }

       return false;*/

       return rear == front;

   }

}


总结


队列是先进先出的数据结构,不要和栈记混淆了.

掌握链表实现栈,以及循环队列的原理

熟悉循环队列中什么情况下队列满了 出队和入队时 front和rear 怎么进行调整

相关文章
|
11月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
938 9
|
6月前
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
181 1
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
9月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
373 77
|
8月前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
192 11
|
8月前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
9月前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
297 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
8月前
|
缓存 监控 算法
内网监控管理软件:PHP 语言队列算法揭秘
在数字化办公环境中,内网监控管理软件对企业的稳定运行和信息安全至关重要。本文深入介绍PHP中的队列算法及其在内网监控软件中的应用,包括监控数据收集、任务调度和日志记录等场景,通过代码示例展示其实现方法。队列算法可提高性能、保证数据顺序并实现异步处理,为企业提供高效的安全保障。
107 1
|
9月前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
157 9
|
9月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
207 7

热门文章

最新文章