队列的链式结构(C语言实现)

5.2 队列的链式结构




5.2.1 队列链式结构简介



相比较与循环队列的顺序存储结构,链式结构多了一条指向下一结点的链,和单链表结构是类似的,只是链式队列只能"头出尾进",下图是一个链式队列的简单结构图:

在这里插入图片描述


引入了一个头结点不存储任何东西,front指针指向头结点,rear指针指向队列的最后一个元素。


下面是涉及到有关链式队列操作的表:

操作函数操作结果
InitQueue(LinkQueue *Q)初始化链队列
DestroyQueue(LinkQueue *Q)销毁队列
ClearQueue(LinkQueue *Q)清空队列
QueueEmpty(LinkQueue Q)判断队列是否为空
QueueLength(LinkQueue Q)求链队列长度
GetHead(LinkQueue Q,QElemType *e)获取头元素,并且存入指针e
EnQueue(LinkQueue *Q,QElemType e)入队
DeQueue(LinkQueue *Q,QElemType *e)出队
QueueTraverse(LinkQueue Q)遍历并且打印队列



5.2.2 队列链式结构定义

链队列的结构定义和单链表类似,一个链式队列包含数个结点和指向头结点,尾结点的指针(front和rear),一个结点包含存储的数据data和指向下一结点的链next

代码如下:

typedef struct QNode{           //结点结构
    QElemType data;
    struct QNode *next;  
}QNode,*QueuePtr;

typedef struct{                 //队列结构
    QueuePtr front,rear;       //队头 队尾指针
}LinkQueue;



QNode为结点类,LinkQueue为链队列类,队头,队尾指针都是结点指针类型。




5.2.3 初始化链队列



一个初始的链队列中不存储任何数据,头尾指针都指向头结点,下图是一个初始化的链队列:

在这里插入图片描述

千万要注意:头结点不等于队头,头结点不存储任何数据,队头是头结点的下一结点,即Q->front->next

代码如下:

/*初始化链队列*/
/*Q->front 是头结点不存任何东西 Q->front->next表示队头*/
Status InitQueue(LinkQueue *Q){
    Q->front=Q->rear=(QueuePtr)malloc(sizeof(QNode));
    if(!Q->front)
        exit(OVERFLOW);
    Q->front->next = NULL;
    return OK;
}




5.2.4 销毁队列和清空队列

销毁队列和清空队列是不同的,销毁队列连同头结点(那个不存储数据的结点),一起删除。而清空队列不删除头结点,只是删除存储数据的结点。


1. 销毁队列

代码如下:

/*销毁队列*/
Status DestroyQueue(LinkQueue *Q){
    /*从头结点开始一直删除到最后 直到链队列不存在为止*/
    while(Q->front){
        Q->rear = Q->front->next;
        free(Q->front);
        Q->front = Q->rear;
    }
    return OK;
}



其中的Q->rear只是起到一个暂时记录Q->front->next结点的作用。第一次循环:Q->rear 记录了队头的位置,然后free(Q->front)释放头结点,最后将头指针指向队头的位置(头结点的下一位置)。随后的循环都以这样的形式进行,从头结点开始删除,直到队列不存在为止。

2. 清空队列



代码如下:

/*清空队列*/
Status ClearQueue(LinkQueue *Q){
    QueuePtr p,q;
    Q->rear = Q->front;
    p = Q->front->next;
    Q->front->next = NULL;
    /*上面头尾结点都指向null 最后再将队列中间的结点一个个释放*/
    while(p){
        q=p;
        p=p->next;
        free(q);
    }

    return OK;
}



清空队列,首先将尾指针指向头结点位置Q-rear = Q->front,然后将队头元素赋值给指针p,即p = Q->front->next,然后再将头结点的next指针赋值为空,这就让头尾结点都变成了初始化的时候,但是中间还有很多队列结点等待释放,这便交给了下面的while循环。




5.2.5 判断链队列是否为空和获取链队列长度


1. 判断链队列是否为空

当头指针和尾指针在同一个位置时,链队列为空,代码如下:

/*判断队列是否为空*/
Status QueueEmpty(LinkQueue Q){ 
	if(Q.front==Q.rear)
		return TRUE;
	else
		return FALSE;
}


2. 获取链队列长度

先使p=Q.front,让p指向头结点,只要Q.rear!=p就使p一直往下移动,并且让i+1,记录长度。

代码如下:

/*求队列长度*/
int QueueLength(LinkQueue Q){
    int i = 0;
    QueuePtr p;
    p = Q.front;
    while(Q.rear!=p){
        i++;
        p=p->next;
    }
    return i;
}




5.2.6 获得链队列队头元素

只要通过头指针就可以轻易获取到队列头元素,代码如下:

/*获取头元素*/
Status GetHead(LinkQueue Q,QElemType *e){
    QueuePtr p;
    if(Q.front==Q.rear)       //队空
        return ERROR;
    p = Q.front->next;
    *e = p->data;
    return OK;
}




5.2.7 入队和出队


1. 入队

链队列入队和单链表的插入是类似的,只不过链队列只能从队尾插入,入队的示意图如下:

在这里插入图片描述

步骤如下:

  • 先生成一个新结点s,并且将数据存入
  • 然后让s结点的next指针指向NULL,即s->next=NULL
  • 再让rear指针处结点的next指针指向s,即Q->rear->next = s
  • 最后将rear指针移动到结点s处Q->rear = s,完成入队

代码如下:

/*链队列入队*/
Status EnQueue(LinkQueue *Q,QElemType e){
    QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
    if(!s)
        exit(OVERFLOW);
    s->data = e;
    s->next = NULL;
    Q->rear->next = s;      //将s赋值给队尾后继元素
    Q->rear = s;            //最后将s赋值给队尾指针

    return OK;
}


2. 出队

出队都是在队头完成的,下面是出队示意图:

在这里插入图片描述

步骤如下:

  • 首先判断队列是否为空
  • 将要出队的队头赋值给p,即p=Q->front->next
  • 然后再将p的后继变成队头,即Q->front->next = p->next
  • 释放结点p

注意,如果删除的队头就是队尾结点,将rear指向头结点。

代码如下:

/*链队列出队*/
Status DeQueue(LinkQueue *Q,QElemType *e){
    QueuePtr p;
    if(Q->front == Q->rear)     //队空
        return ERROR;
    p = Q->front->next;         //将队头将要删除的元素暂时储存在p
    *e = p->data;
    Q->front->next = p->next;   //将p的后继变为队头,之前的队头出队
    if(Q->rear == p)        //若删除的队头就是队尾,将rear指向头结点
        Q->rear = Q->front;
    free(p);
    return OK;
}




5.2.8 遍历打印链队列

代码如下:

/*遍历队列 从队头开始到队尾*/
Status visit(QElemType c){
    printf("%d ",c);
    return OK;
}

Status QueueTraverse(LinkQueue Q){
    QueuePtr p;
    p = Q.front->next;      //p是队头
    while(p){
        visit(p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}




5.2.9 测试代码

测试代码

int main(){
	int i;
	QElemType d;
	LinkQueue q;
	i=InitQueue(&q);
	if(i)
		printf("成功地构造了一个空队列!\n");
	printf("是否空队列?%d(1:空 0:否)  ",QueueEmpty(q));
	printf("队列的长度为%d\n",QueueLength(q));
	EnQueue(&q,-5);
	EnQueue(&q,5);
	EnQueue(&q,10);
	printf("插入3个元素(-5,5,10)后,队列的长度为%d\n",QueueLength(q));
	printf("是否空队列?%d(1:空 0:否)  ",QueueEmpty(q));
	printf("队列的元素依次为:");
	QueueTraverse(q);
	i=GetHead(q,&d);
	if(i==OK)
	 printf("队头元素是:%d\n",d);
	DeQueue(&q,&d);
	printf("删除了队头元素%d\n",d);
	i=GetHead(q,&d);
	if(i==OK)
		printf("新的队头元素是:%d\n",d);
	ClearQueue(&q);
	printf("清空队列后,q.front=%u q.rear=%u q.front->next=%u\n",q.front,q.rear,q.front->next);
	DestroyQueue(&q);
	printf("销毁队列后,q.front=%u q.rear=%u\n",q.front, q.rear);
	system("pause");
	return 0;
}


运行结果

成功地构造了一个空队列!
是否空队列?1(1:空 0:否)  队列的长度为0
插入3个元素(-5,5,10)后,队列的长度为3
是否空队列?0(1:空 0:否)  队列的元素依次为:-5 5 10
队头元素是:-5
删除了队头元素-5
新的队头元素是:5
清空队列后,q.front=13060336 q.rear=13060336 q.front->next=0
销毁队列后,q.front=0 q.rear=0
请按任意键继续. . .




5.2.10 完整代码

下面附上完整代码:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20

typedef int Status; 
typedef int QElemType;

typedef struct QNode{           //结点结构
    QElemType data;
    struct QNode *next;  
}QNode,*QueuePtr;

typedef struct{                 //队列结构
    QueuePtr front,rear;       //队头 队尾指针
}LinkQueue;

/*初始化链队列*/
/*Q->front 是头结点不存任何东西 Q->front->next表示队头*/
Status InitQueue(LinkQueue *Q){
    Q->front=Q->rear=(QueuePtr)malloc(sizeof(QNode));
    if(!Q->front)
        exit(OVERFLOW);
    Q->front->next = NULL;
    return OK;
}

/*销毁队列*/
Status DestroyQueue(LinkQueue *Q){
    /*从头结点开始一直删除到最后 直到链队列不存在为止*/
    while(Q->front){
        Q->rear = Q->front->next;
        free(Q->front);
        Q->front = Q->rear;
    }
    return OK;
}

/*清空队列*/
Status ClearQueue(LinkQueue *Q){
    QueuePtr p,q;
    Q->rear = Q->front;
    p = Q->front->next;
    Q->front->next = NULL;
    /*上面头尾结点都指向null 最后再将队列中间的结点一个个释放*/
    while(p){
        q=p;
        p=p->next;
        free(q);
    }

    return OK;
}

/*判断队列是否为空*/
Status QueueEmpty(LinkQueue Q){ 
	if(Q.front==Q.rear)
		return TRUE;
	else
		return FALSE;
}


/*求队列长度*/
int QueueLength(LinkQueue Q){
    int i = 0;
    QueuePtr p;
    p = Q.front;
    while(Q.rear!=p){
        i++;
        p=p->next;
    }
    return i;
}

/*获取头元素*/
Status GetHead(LinkQueue Q,QElemType *e){
    QueuePtr p;
    if(Q.front==Q.rear)       //队空
        return ERROR;
    p = Q.front->next;
    *e = p->data;
    return OK;
}

/*链队列入队*/
Status EnQueue(LinkQueue *Q,QElemType e){
    QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
    if(!s)
        exit(OVERFLOW);
    s->data = e;
    s->next = NULL;
    Q->rear->next = s;      //将s赋值给队尾后继元素
    Q->rear = s;            //最后将s赋值给队尾指针

    return OK;
}

/*链队列出队*/
Status DeQueue(LinkQueue *Q,QElemType *e){
    QueuePtr p;
    if(Q->front == Q->rear)     //队空
        return ERROR;
    p = Q->front->next;         //将队头将要删除的元素暂时储存在p
    *e = p->data;
    Q->front->next = p->next;   //将p的后继变为队头,之前的队头出队
    if(Q->rear == p)        //若删除的队头就是队尾,将rear指向头结点
        Q->rear = Q->front;
    free(p);
    return OK;
}

/*遍历队列 从队头开始到队尾*/
Status visit(QElemType c){
    printf("%d ",c);
    return OK;
}

Status QueueTraverse(LinkQueue Q){
    QueuePtr p;
    p = Q.front->next;      //p是队头
    while(p){
        visit(p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}

int main(){
	int i;
	QElemType d;
	LinkQueue q;
	i=InitQueue(&q);
	if(i)
		printf("成功地构造了一个空队列!\n");
	printf("是否空队列?%d(1:空 0:否)  ",QueueEmpty(q));
	printf("队列的长度为%d\n",QueueLength(q));
	EnQueue(&q,-5);
	EnQueue(&q,5);
	EnQueue(&q,10);
	printf("插入3个元素(-5,5,10)后,队列的长度为%d\n",QueueLength(q));
	printf("是否空队列?%d(1:空 0:否)  ",QueueEmpty(q));
	printf("队列的元素依次为:");
	QueueTraverse(q);
	i=GetHead(q,&d);
	if(i==OK)
	 printf("队头元素是:%d\n",d);
	DeQueue(&q,&d);
	printf("删除了队头元素%d\n",d);
	i=GetHead(q,&d);
	if(i==OK)
		printf("新的队头元素是:%d\n",d);
	ClearQueue(&q);
	printf("清空队列后,q.front=%u q.rear=%u q.front->next=%u\n",q.front,q.rear,q.front->next);
	DestroyQueue(&q);
	printf("销毁队列后,q.front=%u q.rear=%u\n",q.front, q.rear);
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

压力小子呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值