数据结构——队列queue(c)-受限线性表

👍数据结构——队列queue(c)-受限线性表


一、队列定义

队列 是仅限定在表尾进行插入和表头进行删除操作的线性表。

特点 先进先出。(故又称为先进先出表FIFO-First In First Out)

相关定义
队尾 允许插入的一端(两种情况:(1)队尾元素;(2)队尾的下一位)
队头 允许删除的一端
空队列
入队 EnQueue
出队 DeQueue

二、队列的顺序存储结构——循环队列

1.基于队尾指针指向队尾元素的下一位

顺序队列不循环时存在什么问题?
答:从创建空的队列开始

Status InitQueue(SqQueue &Q){
	Q.base = new QElemtype [MAXSIZE];
	if(!Q.base) return overflow;
	Q.front = Q.rear = 0;
	return success;
}

易知:
判空的条件: Q.rear == Q.front = 0;
判满的条件:Q.rear == MAXSIZE;
队列不满时,新元素new_element入队:Q.base[Q.rear] = new_element; Q.rear++;
队列长度:Q.rear - Q.front
队头元素:Q.base[Q.front]
队尾元素:Q.base[Q.rear-1]
队列不空时,队头元素出队: Exit_element = Q.base[Q.front]; Q.front++;
易知,容易造成假溢出。

解决方法:将队列空间看成是逻辑上的环状空间!
如下:
(1)当Q.rear == MAXSIZE;时,将其置为0;
(2)或当Q.rear == Q.rear % MAXSIZE;
但此时,队列空/队列满均为Q.rear == Q.front

解决方法:

  1. 在定义中设置一个队列长度计数器Q.length
    初始值为0(队列空)。
    当一个元素入队列时+1,出队列时-1。
    队列空:Q.length == MAXSIZE
    队列满:Q.length == 0
    队列长度:Q.length
  2. 令队列空间中的一个单元闲置,使得在任何时刻,保持Q.rear和Q.front之间至少隔一个空闲单元。
    队列空:Q.rear == Q.front
    队列满:Q.front == (Q.rear + 1) % MAXSIZE
    队列长度:( Q.rear - Q.front + MAXSIZE ) % MAXSIZE
  3. 在定义中设置一个变量tag,每次删除操作成功时,tag=0;每次插入操作成功时,tag=1
    队列空:Q.rear == Q.front && Q.tag == 0
    队列满:Q.rear == Q.front && Q.tag == 1
    队列长度:队列不满时:((Q.rear - Q.front + MAXSIZE)) % MAXSIZE队列满时:MAXSIZE

具体实现

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdbool.h>
#define MAXSIZE 100

typedef struct SqQueue{
    int *base;
    int front;
    int rear;
}SqQueue;

void InitQueue(SqQueue *Q); //初始化队列Q
bool QueueEmpty(SqQueue Q); //判断队列是否为空
void EnQueue(SqQueue *Q, int e); //入队
int DeQueue(SqQueue *Q); //出队
int GetFront(SqQueue *Q); //获取队头元素,但不从队列中移走该元素
int length(SqQueue Q); //计算并返回队列中元素的个数

//初始化队列Q
void InitQueue(SqQueue *Q){
    Q->base = (int *)malloc(MAXSIZE * sizeof(int));
    if(!Q->base){
        printf("内存分配失败!");
        return;
    }
    Q->front = Q->rear = 0;
}
//判断队列是否为空
bool QueueEmpty(SqQueue Q){
    if(Q.front == Q.rear)
        return true;
    else
        return false;
}
//入队
void EnQueue(SqQueue *Q, int e){
    if((Q->rear+1)%MAXSIZE == Q->front){
        printf("队列满!");
        return;
    }
    Q->base[Q->rear] = e;
    Q->rear = (Q->rear+1)%MAXSIZE;
}
//出队
int DeQueue(SqQueue *Q){
    if(Q->front == Q->rear){
        printf("队列空!");
        return 0;
    }
    int e = Q->base[Q->front];
    Q->front = (Q->front + 1)%MAXSIZE;
    return e;
}
//获取队头元素,但不从队列中移走该元素
int GetFront(SqQueue *Q){
    if(Q->front == Q->rear){
        printf("队列空!");
        return 0;
    }
    return Q->base[Q->front];
}
//计算并返回队列中元素的个数
int length(SqQueue Q){
    return (Q.rear - Q.front + MAXSIZE)%MAXSIZE;
}

int main()
{
    SqQueue Q;
    InitQueue(&Q);
    for(int i = 0; i < 10; i++){
        EnQueue(&Q, i);
    }
    printf("%d ", GetFront(&Q));
    printf("此时长度为%d\n", length(Q));
    DeQueue(&Q);
    printf("%d ", GetFront(&Q));
    printf("此时长度为%d\n", length(Q));
    return 0;
}

2.基于队尾指针指向队尾元素

为了使队列中的全部元素入队列,出队列等相关操作一致,可在初始化队列时,使front指向队头,rear指向队头的前一个位置。

  1. 在定义中设置一个队列长度计数器Q.length
    初始值为0(队列空)。
    当一个元素入队列时+1,出队列时-1。
    队列空:Q.length == MAXSIZE
    队列满:Q.length == 0
    队列长度:Q.length
  2. 令队列空间中的一个单元闲置,使得在任何时刻,保持Q.rear和Q.front之间至少隔一个空闲单元。
    队列空:(Q.rear + 1) % MAXSIZE == Q.front
    队列满:Q.front == (Q.rear + 2) % MAXSIZE
    队列长度:( Q.rear + 1 - Q.front + MAXSIZE ) % MAXSIZE
  3. 在定义中设置一个变量tag,每次删除操作成功时,tag=0;每次插入操作成功时,tag=1
    队列空:(Q.rear + 1) % MAXSIZE == Q.front && Q.tag == 0
    队列满:(Q.rear + 1) % MAXSIZE == Q.front && Q.tag == 1
    队列长度:队列不满时:((Q.rear + 1 - Q.front + MAXSIZE)) % MAXSIZE队列满时:MAXSIZE

三、队列的链式存储实现——链队列

显然需要两个分别指向队头和队尾的指针才能惟一确定。
优点:一般来说不会队满,除非内存不足。
注:
①对于链式存储实现队列,分为带头结点和不带头结点两种。
②对于入队、出队操作,注意第一个元素入队和最后一个元素出队的操作。
注:下述内容基于带头结点的单链表,队列队尾指针指向队尾元素

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdbool.h>

//队列内元素存储结构
typedef struct QNode{
    int data;
    struct QNode *next;
} QNode, *QueuePtr;
//队列类型
typedef struct{
    QueuePtr front;
    QueuePtr rear;
}LinkQueue;

void InitQueue(LinkQueue *Q); //初始化队列Q(带头结点)
bool QueueEmpty(LinkQueue Q); //判断队列是否为空
void EnQueue(LinkQueue *Q, int e); //将元素e放入队尾
int DeQueue(LinkQueue *Q); //移走队头元素,返回该元素的值
int GetFront(LinkQueue Q); //获取队头元素的值,但不从队列中移走该元素
int Length(LinkQueue Q); //计算并返回队列中元素的个数

//初始化队列Q(带头结点)
void InitQueue(LinkQueue *Q){
	//初始化时front、rear都指向头结点
    Q->front = Q->rear = (QueuePtr)malloc(sizeof(QNode));
    if(!Q->front) return;
    Q->front->next = NULL;
}

//判断队列是否为空
bool QueueEmpty(LinkQueue Q){
    if(Q.front == Q.rear){
        return true;
    }
    else
        return false;
}

//将元素e放入队尾
void EnQueue(LinkQueue *Q, int e){
    QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
    if(!p) return;
    p->data = e;
    p->next = NULL;
    Q->rear->next = p;
    Q->rear = p;
}

//移走队头元素,返回该元素的值
int DeQueue(LinkQueue *Q){
	//判断队列是否为空
    if(Q->rear == Q->front){
        printf("队列为空!");
        return 0;
    }
    QueuePtr p = Q->front->next;
    int e = p->data; //用变量e存放队头元素
    Q->front->next = p->next; //修改头结点的next指针
    if(Q->rear == p) //若此次出队列的使队列中的最后一个结点
        Q->rear = Q->front; //修改rear指针
    free(p); //释放结点空间
    return e;
}

//获取队头元素的值,但不从队列中移走该元素
int GetFront(LinkQueue Q){
    return Q.front->next->data;
}

//计算并返回队列中元素的个数
int Length(LinkQueue Q){
    int n = 0;
    QueuePtr p;
    p = Q.front;
    while(p != Q.rear){
        p = p->next;
        n++;
    }
    return n;
}


int main()
{
    LinkQueue Q;
    InitQueue(&Q);
    for(int i = 0; i < 10; i++){
        EnQueue(&Q, i);
    }
    printf("%d ", Length(Q));
    printf("%d", GetFront(Q));
    return 0;
}

四、双端队列

双端队列:只允许从两端插入、两端删除的线性表。
输入受限的双端队列:只允许一端插入、两端删除的线性表。
输出受限的双端队列:只允许两端插入、一端删除的线性表。
:对输出序列合法性的判断
  在栈中合法的输出序列,在双端队列中必定合法

五、应用

1. 离散时间模拟

2.排队问题

3.作业控制

4.树的层次遍历

5.图的广度优先搜索

6.在操作系统中的应用:多个进程争抢着使用有限的系统资源时,FCFS(First Come First Service,先来先服务)是一种常用策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值