当前位置:   article > 正文

队列存储结构的实现和基本操作_qnode* initqueue()

qnode* initqueue()


1. 什么是队列

队列具有以下两个特点

  • 数据从队列的一端进,另一端出
  • 数据的入队和出队遵循"先进先出"的原则
    在这里插入图片描述
    队列存储结构的实现有以下两种方式:
  • 顺序队列:在顺序表的基础上实现的队列结构
  • 链队列:在链表的基础上实现的队列结构

2. 顺序队列简单实现

2.1 数组方式实现队列

顺序队列的底层使用的是数组,需预先申请一块足够大的内存空间初始化顺序队列。除此之外,为了满足顺序队列中数据从队尾进,队头出且先进先出的要求,还需要定义两个位置指针(toprear)分别用于指向顺序队列中的队头元素和队尾元素,如图所示:
在这里插入图片描述
由于顺序队列初始状态没有存储任何元素,因此 top 指针和 rear 指针重合,且由于顺序队列底层实现靠的是数组,因此 toprear 实际上是两个变量,它的值分别是队头元素和队尾元素所在数组位置的下标

入队和出队操作的实现思想:

当有数据元素进队列时,对应的实现操作是将元素存储在指针 rear 指向的数组位置,然后 rear+1;当需要队头元素出队时,仅需top+1 操作。

例如,将 {1,2,3,4} 用顺序队列存储的实现操作如图所示:
在这里插入图片描述

{1,2,3,4} 入队后,将顺序队列中数据出队的实现过程如图所示:
在这里插入图片描述
因此,使用顺序表实现顺序队列C 语言实现代码为:

#include <stdio.h>
int enQueue(int *a,int rear,int data)
{
    a[rear]=data;
    rear++;
    return rear;
}

void deQueue(int *a,int front,int rear)
{
    //如果 front==rear,表示队列为空
    while (front!=rear) 
    {
        printf("出队元素:%d\n",a[front]);
        front++;
    }
}

int main() 
{
    int a[100];
    int front,rear;
    //设置队头指针和队尾指针,当队列中没有元素时,队头和队尾指向同一块地址
    front=rear=0;
    //入队
    rear=enQueue(a, rear, 1);
    rear=enQueue(a, rear, 2);
    rear=enQueue(a, rear, 3);
    rear=enQueue(a, rear, 4);
    //出队
    deQueue(a, front, rear);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

程序输出结果:

出队元素:1
出队元素:2
出队元素:3
出队元素:4
  • 1
  • 2
  • 3
  • 4

此方法存在的问题:
将所有数据入队再出队后,指针 toprear 重合位置指向了 a[4] 而不再是 a[0]。也就是说,整个顺序队列在数据不断地进队出队过程中,在顺序表中的位置不断后移。

顺序队列整体后移造成的影响是:

  • 顺序队列之前的数组存储空间将无法再被使用,造成了空间浪费;
  • 如果顺序表申请的空间不足够大,则直接造成程序中数组 a 溢出,产生溢出错误;

为了避免以上两点,可以使用下面的方法实现顺序队列。

2.2 顺序队列另一种实现方法

为了解决以上两个问题,可以使用巧妙的方法将顺序表打造成一个环状表,如图所示:
在这里插入图片描述
上图只是一个想象图,在真正的实现时,没必要真创建这样一种结构,我们还是使用之前的顺序表,也还是使用之前的程序,只需要对其进行一点小小的改变:

#include <stdio.h>
#define max 5//表示顺序表申请的空间大小
int enQueue(int *a,int front,int rear,int data)
{
    //添加判断语句,如果rear超过max,则直接将其从a[0]重新开始存储,如果rear+1和front重合,则表示数组已满
    if ((rear+1)%max==front) 
    {
        printf("空间已满");
        return rear;
    }
    a[rear%max]=data;
    rear++;
    return rear;
}
int  deQueue(int *a,int front,int rear)
{
    //如果front==rear,表示队列为空
    if(front==rear%max) 
    {
        printf("队列为空");
        return front;
    }
    printf("%d ",a[front]);
    //front不再直接 +1,而是+1后同max进行比较,如果=max,则直接跳转到 a[0]
    front=(front+1)%max;
    return front;
}
int main() 
{
    int a[max];
    int front,rear;
    //设置队头指针和队尾指针,当队列中没有元素时,队头和队尾指向同一块地址
    front=rear=0;
    //入队
    rear=enQueue(a,front,rear, 1);
    rear=enQueue(a,front,rear, 2);
    rear=enQueue(a,front,rear, 3);
    rear=enQueue(a,front,rear, 4);
    //出队
    front=deQueue(a, front, rear);
    //再入队
    rear=enQueue(a,front,rear, 5);
    //再出队
    front=deQueue(a, front, rear);
    //再入队
    rear=enQueue(a,front,rear, 6);
    //再出队
    front=deQueue(a, front, rear);
    front=deQueue(a, front, rear);
    front=deQueue(a, front, rear);
    front=deQueue(a, front, rear);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

程序运行结果:

1 2 3 4 5 6
  • 1

使用此方法需要注意的是,顺序队列在判断数组是否已满时,出现下面情况:

  • 当队列为空时,队列的头指针等于队列的尾指针;
  • 当数组满员时,队列的头指针等于队列的尾指针;

顺序队列的存储状态不同,但是判断条件相同。为了对其进行区分,最简单的解决办法是:牺牲掉数组中的一个存储空间。判断数组满员的条件是:尾指针的下一个位置和头指针相遇,就说明数组满了,即程序中第 5 行所示。

3. 链式队列及基本操作

链式队列的实现思想同顺序队列类似,只需创建两个指针(命名为 toprear)分别指向链表中队列的队头元素和队尾元素,如图所示:
在这里插入图片描述
上图所示为链式队列的初始状态,此时队列中没有存储任何数据元素,因此 toprear指针都同时指向头节点。

由此,我们可以编写出创建链式队列的 C 语言实现代码为:

//链表中的节点结构
typedef struct QNode
{
    int data;
    struct QNode * next;
}QNode;
//创建链式队列的函数
QNode * initQueue()
{
    //创建一个头节点
    QNode * queue=(QNode*)malloc(sizeof(QNode));
    //对头节点进行初始化
    queue->next=NULL;
    return queue;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3.1 链式队列数据入队

链队队列中,当有新的数据元素入队,只需进行以下 3 步操作:

  1. 将该数据元素用节点包裹,例如新节点名称为 elem
  2. rear 指针指向的节点建立逻辑关系,即执行 rear->next=elem
  3. 最后移动 rear 指针指向该新节点,即 rear=elem

依次将 {1,2,3} 依次入队,各个数据元素入队的过程如图所示:
在这里插入图片描述
数据元素入链式队列的 C 语言实现代码为:

QNode* enQueue(QNode * rear,int data)
{
    //1、用节点包裹入队元素
    QNode * enElem=(QNode*)malloc(sizeof(QNode));
    enElem->data=data;
    enElem->next=NULL;
    //2、新节点与rear节点建立逻辑关系
    rear->next=enElem;
    //3、rear指向新节点
    rear=enElem;
    //返回新的rear,为后续新元素入队做准备
    return rear;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.2 链式队列数据出队

有数据元素需要出队时,按照 “先进先出” 的原则,只需将存储该数据的节点以及它之前入队的元素节点按照原则依次出队即可。这里,我们先学习如何将队头元素出队。

链式队列中队头元素出队,需要做以下 3 步操作:

  1. 通过 top 指针直接找到队头节点,创建一个新指针 p 指向此即将出队的节点;
  2. 将 p 节点(即要出队的队头节点)从链表中摘除;
  3. 释放节点 p,回收其所占的内存空间;

将元素 1 和 2 出队操作过程如图所示:

在这里插入图片描述
链式队列中队头元素出队的 C 语言实现代码为:

void DeQueue(QNode * top,QNode * rear)
{
    if (top->next==NULL) 
    {
        printf("队列为空");
        return ;
    }
    // 1、
    QNode * p=top->next;
    printf("%d",p->data);
    top->next=p->next;
    if (rear==p) 
    {
        rear=top;
    }
    free(p);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

给出链式队列入队和出队的完整 C 语言代码为:

#include <stdio.h>
#include <stdlib.h>
typedef struct QNode
{
    int data;
    struct QNode * next;
}QNode;
QNode * initQueue()
{
    QNode * queue=(QNode*)malloc(sizeof(QNode));
    queue->next=NULL;
    return queue;
}
QNode* enQueue(QNode * rear,int data)
    QNode * enElem=(QNode*)malloc(sizeof(QNode));
    enElem->data=data;
    enElem->next=NULL;
    //使用尾插法向链队列中添加数据元素
    rear->next=enElem;
    rear=enElem;
    return rear;
}
QNode* DeQueue(QNode * top,QNode * rear)
{
    if (top->next==NULL) 
    {
        printf("\n队列为空");
        return rear;
    }
    QNode * p=top->next;
    printf("%d ",p->data);
    top->next=p->next;
    if (rear==p) 
    {
        rear=top;
    }
    free(p);
    return rear;
}
int main() 
{
    QNode * queue,*top,*rear;
    queue=top=rear=initQueue();//创建头结点
    //向链队列中添加结点,使用尾插法添加的同时,队尾指针需要指向链表的最后一个元素
    rear=enQueue(rear, 1);
    rear=enQueue(rear, 2);
    rear=enQueue(rear, 3);
    rear=enQueue(rear, 4);
    //入队完成,所有数据元素开始出队列
    rear=DeQueue(top, rear);
    rear=DeQueue(top, rear);
    rear=DeQueue(top, rear);
    rear=DeQueue(top, rear);
    rear=DeQueue(top, rear);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

程序运行结果为:

1 2 3 4
队列为空
  • 1
  • 2
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/918142
推荐阅读
相关标签
  

闽ICP备14008679号