数据结构链式队列的C语言实现

队列是区别与栈的一种线性表。栈的核心是LIFO,即后进先出,栈最常用的是顺序实现,用一片连续 的地址存储栈。而队列是一种FIFO也就是先进先出的线性表,一个队列有且仅有一个队头和一个队尾,元素只能由队尾进入,从队头排出。而队列比较常用的是链式结构实现的队列。我们可以想一想为什么链式比顺序结构更加适合于队列。原因就在于队列是先进先出,而顺序实现申请到的地址是返回首元素地址的(malloc函数)这个首地址是不宜变动的,否则会带来一些列的麻烦,而栈干好是先进后出,第一个进入的元素出栈的可能性是最小的,只有上面所有的元素都出栈了才轮到她,而咋i队列中,第一个进入的元素或者说队头元素必然是最先出队列的,所以如果用顺序结构实现队列,申请到的首地址只要经历过一次出队列的活动后,就不再是指向队列对头的指针了,除非把队列中的元素全部向前挪动一位,而然这样做的代价是巨大了,正是由于基地址或者说首地址(队头元素的地址)变化频繁,导致顺序结构不是现实队列的好方式。而链表就成了队列实现的首选项了。
队列的实现在线性表中是比较复杂的,在普通的顺序实现的线性表中,我们只需要这样的一个结构:

typedef struct {
	int *elem;
	int length;
	int size;
}Sqlist;

我们用elem来存申请到的空间的首地址,size来记录当前申请到的空间的大小(注意不表示线性表中元素的个数),length才是记录当前线性表中以存储的元素个数的。
而普通的链式线性表的基本结构是:

typedef struct LNode{
	int data;
	struct LNode *next;
}LNode,*LinkList;

我们把数据存在上面的结构体结点中的data区域,而利用指针next来存下一个结点的位置。
而静态链表中,我们使用的结构是这样的:

typedef struct{
	char data;
	int cur;
}component,SLinkList[MAX];

我们使用一个结构体数组,用游标记录逻辑上下一个元素在结构体数组中的位置,而实际的数据存储在data中。(这是不常见的线性表,用于没有指针而又需要链表的高级程序设计语言,缺点是线性表的大小固定,一个致命的弱点,不是吗?)
然后是栈的存储结构:

typedef struct {
	char *top;
	char *base;
	int size;
}SqStack;

我们在上面也讲到过,真是由于栈最先进入的元素出栈可能性最低,导致栈很契合于顺序实现,我们的这个结构体中,用base记录申请到的空间的首地址,top记录栈顶元素上面一个空白区域的地址(注意 top可不是指向栈顶元素的,top–才是),用size来记录当前栈含有的空间大小(注意不是栈中存储了的元素个数,那个是s->top-s->base)
而队列的存储结构就比较复杂了,或者说像上面多有实现的结构的综合体:

typedef struct QNode{
	int data;
	struct QNode *next;
}QNode,*QueuePtr;

typedef struct {
	QueuePtr front;
	QueuePtr rear;
}LinkQueue;

可以看到我们至少需要两个结构体(存储的元素是基本类型而非结构体类型)才能实现链式队列。我们首先来分析一下这两个结构体的作用。QNode比较容易看出来。它是链表中的结点,数据存储在它的data域,而next是它的指针域指向下一个结点。而LinkQueue结构体含有两个指向QNode结构体的指针变量,其中front指向链表的头结点,rear指向链表的尾结点,所以看起来复杂其实很简单,链式队列就是一个链表,然后把这个链表的头结点地址和尾结点地址封装在一个结构体里面,就成了链式队列。
示意图
理解了链式队列的结构后,我们就可以动手来完成这个ADT了。附上C语言实现的代码:

#include<stdio.h>
#include<stdlib.h>
#define ERROR 0
#define OK 1

typedef int Status;

typedef struct QNode{
	int data;
	struct QNode *next;
}QNode,*QueuePtr;

typedef struct {
	QueuePtr front;
	QueuePtr rear;
}LinkQueue; 

int main(){
	Status InitQueue(LinkQueue *);
	Status DestroyQueue(LinkQueue *);
	Status EnQueue(LinkQueue *,int );
	Status DeQueue(LinkQueue *,int *);
	Status ClearQueue(LinkQueue *);
	Status QueueEmpty(LinkQueue *);
	Status GetTop(LinkQueue *,int *);
	int QueueLength(LinkQueue *q);
	void visit(int );
	Status QueueTraverse(LinkQueue *,void (*f)(int ));
	
	LinkQueue q,*qp=&q;
	if(InitQueue(qp)){
		printf("初始化成功!\n");
	}
	int a=9,b,*bp=&b;
	printf("常数8进入队列\n");
	EnQueue(qp,8);
	printf("变量9进入队列\n");
	EnQueue(qp,a);
	printf("当前的队列长度为:%d\n",QueueLength(qp));
	printf("遍历当前队列:");
	QueueTraverse(qp,visit);
	GetTop(qp,bp);
	printf("\n获取队列头的元素(但是不出队列):%d\n",b);
	printf("遍历当前队列:");
	QueueTraverse(qp,visit);
	printf("\n当前的队列长度为:%d\n",QueueLength(qp));
	DeQueue(qp,bp);
	printf("弹出位于队列头的元素:%d\n",b);
	printf("当前的队列长度为:%d\n",QueueLength(qp));
	printf("遍历当前队列:");
	QueueTraverse(qp,visit);
	DeQueue(qp,bp);
	printf("\n弹出位于队列头的元素:%d\n",b);
	printf("当前的队列长度为:%d\n",QueueLength(qp));
	printf("遍历当前队列:");
	QueueTraverse(qp,visit);
	if(QueueEmpty(qp)){
		printf("\n这是一个空队列\n");
	}
	if(DestroyQueue(qp)){
		printf("链队列销毁成功!\n");
	}
	return 0;
}

Status InitQueue(LinkQueue *q){
	q->front=(QueuePtr)malloc(sizeof(QNode));
	if(!q->front){
		exit(1);
	}
	q->rear=q->front;
	q->rear->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 EnQueue(LinkQueue *q,int e){
	QueuePtr p=(QueuePtr)malloc(sizeof(QNode));
	if(!p){
		exit(1);
	}
	p->data=e;
	p->next=NULL;
	q->rear->next=p;
	q->rear=p;
	return OK;
}

Status DeQueue(LinkQueue *q,int *e){
	if(q->front==q->rear){
		return ERROR;
	}
	QueuePtr p=q->front->next;
	*e=p->data;
	q->front->next=p->next;
	if(q->rear==p){
		q->rear=q->front;
	} 
	free(p);
	return OK;
}

Status ClearQueue(LinkQueue *q){
	q->rear=q->front;
	return OK;
}

Status QueueEmpty(LinkQueue *q){
	if(q->front==q->rear){
		return 1;
	}else{
		return 0;
	}
}

Status GetTop(LinkQueue *q,int *e){
	if(q->front==q->rear){
		return ERROR;
	}
	QueuePtr p=q->front->next;
	*e=p->data;
	return OK;
}

int QueueLength(LinkQueue *q){
	QueuePtr p=q->front;
	int n=0;
	while(p!=q->rear){
		n++;
		p=p->next;
	}
	return n;
}

void visit(int a){
	printf("%d\t",a);
}

Status QueueTraverse(LinkQueue *q,void (*f)(int )){
	if(q->front==q->rear){
		return ERROR;
	}
	QueuePtr p=q->front->next;
	while(p!=q->rear){
		f(p->data);
		p=p->next;
	}
	f(q->rear->data);
	return OK;
}
运行结果:
初始化成功!
常数8进入队列
变量9进入队列
当前的队列长度为:2
遍历当前队列:8  9
获取队列头的元素(但是不出队列):8
遍历当前队列:8  9
当前的队列长度为:2
弹出位于队列头的元素:8
当前的队列长度为:1
遍历当前队列:9
弹出位于队列头的元素:9
当前的队列长度为:0
遍历当前队列:
这是一个空队列
链队列销毁成功!

--------------------------------
Process exited after 0.1394 seconds with return value 0
请按任意键继续. . .

为了简化,我这里的链式队列存储的int型。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

KOKO银角大王

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

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

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

打赏作者

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

抵扣说明:

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

余额充值