C语言-队列(链式队列,循环队列)

目录

链式队列:

1.1结构定义

1.2完整代码

循环队列:

2.1结构定义

2.2 实现思想

2.3完整代码

测试输出:


区别:

这里先对链式队列和循环队列进行一个简单的比较。如果用户的应用程序中设有循环队列,则必须为它设定一个固定的最大队列长度,这样就可能存在栈满的问题;若用户无法预估所用队列的最大长度,则宜采用链队列,链式队列不存在栈满的问题

链式队列:

1.1结构定义

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

typedef struct {
	QueuePtr front;//头指针
	QueuePtr rear;//尾指针
} LinkQueue;

1.2完整代码

易错:在清空队列的时候要将头指针的next域设为NULL

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char QElemType;

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

typedef struct {
	QueuePtr front;//头指针
	QueuePtr rear;//尾指针
} LinkQueue;

int InitQueue(LinkQueue *Q) {//初始化链表队列
	QueuePtr node;
	node = (QueuePtr)malloc(sizeof(QNode));
	if (node != NULL) {
		node->next = NULL; //创建头指针
		Q->front = Q->rear = node;
		return 1;
	} else
		return 0;
}

void DestroyQueue(LinkQueue *Q) {//销毁链表
	QueuePtr temp;
	while (Q->front != Q->rear) {
		temp = Q->front->next;
		free(Q->front);
		Q->front = temp;
	}
	free(Q->rear);
}

void ClearQueue(LinkQueue *Q) { //保留队列头指针和队列尾指针,其他结点销毁
	QueuePtr p, q;
	Q->rear = Q->front; //将尾指针归位到头指针
	q = Q->front->next; //队列头
	while (q != NULL) {
		p = q->next;
		free(q);
		q = p;
	}
Q->front->next = NULL;//天哪一定要记得把头节点next域设为NULL,不然还是被释放了的q,就乱指了
}

int QueueEmpty(LinkQueue Q) { //是否为空,为空返回,非空返回0
	if (Q.front->next == NULL)
		return 1;
	else
		return 0;
}

int QueueLength(LinkQueue Q) { //返回队列长度
	QueuePtr p = Q.front->next;
	int count = 1;
	while (p != Q.rear) {
		count++;
		p = p->next;
	}
	return count;
}

QElemType GetHead(LinkQueue Q) { //若队列非空,返回队列首的值
	if (QueueEmpty(Q) != 1)
		return Q.front->next->data;
	else
		printf("队列为空!\n");
}

void EnQueue(LinkQueue *Q, QElemType e) { //将e元素插入队列
	QueuePtr node;
	node = (QueuePtr)malloc(sizeof(QNode));
	if (node != NULL) {
		node->next = NULL;
		node->data = e;
		Q->rear->next = node;
		Q->rear = node;
	} else
		printf("EnQueue error!\n");
}

QElemType DeQueue(LinkQueue *Q) { //删除队头元素,并返回其值
	QElemType e;
	QueuePtr q;
	if (QueueEmpty(*Q) != 1) {
		q = Q->front->next;
		e = Q->front->next->data;
		Q->front->next = q->next;
		if (Q->rear == q)//队头等于队尾
			Q->rear = Q->front;
		free(q);
		return e;
	} else
		printf("队列为空!\n");
}

void QueueTraverse(LinkQueue Q) { //从队列头到尾遍历队列元素并输出
	QueuePtr p = Q.front->next;
	printf("从队列头到尾遍历队列元素并输出:\n");
	if (QueueEmpty(Q) == 1) {
		printf("队列为空!\n");
		return;
	}
	while (p != NULL) {
		if (p != Q.rear)
			printf("%c ", p->data);
		else
			printf("%c", p->data);
		p = p->next;
	}
}

循环队列:

2.1结构定义

typedef struct {
	QElemType *data; //指向队列储存空间
	int front; //队首下标
	int rear;  //队尾下标
} SqQueue;

2.2 实现思想

(1)循环

要在已经确定大小的数组中实现循环,需要用到这个公式:(求模的方法)

t=(t+1)%SUM(代替了t++)(SUM是数组中元素的个数)

(2)入队和出队

首先,对于一个循环的队列,可以看看下图(参考懒猫老师的《数据结构》相关课程笔记)

元素进入,通过rear尾指针添加到队列:rear先向后移动一位,添加元素

元素退出,通过front首指针退出队列:直接移动front指针向后一位

这里注意,队列中的元素只有从front指针到rear指针(定向的)中间所有的数据,而rear到front则不是,所以想要退出队列,移动front指针,不用对要退出的元素进行处理(像链队列就要释放),后面新添进来的元素会因为rear指针的移动覆盖掉之前的元素。

(3)栈满和栈空的区别

因为栈满和栈空时,头尾指针都会重叠,所以有三种方法来进行区分

 下面的代码采用的时第二种,此时两指针关系如图:即front指针指的区域不算做队列内容,图中显示为空,事实上也可能是一个已经弹出队列的元素,总之对队列本身不构成影响

 即栈空:front==rear,栈满: (rear+1)%MAXQSIZE==front

2.3完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXQSIZE 100//最大队列长度
typedef char QElemType;

typedef struct {
	QElemType *data; //指向队列储存空间
	int front; //队首下标
	int rear;  //队尾下标
} SqQueue;

int InitQueue(SqQueue *Q) {//初始化队列函数,初始化成功返回1,失败返回0
	Q->data = (QElemType *)malloc(MAXQSIZE * sizeof(QElemType));
	if (Q->data != NULL) {
		Q->front = Q->rear = 0;
		return 1;
	}
	return 0;
}

void DestroyQueue(SqQueue *Q) { //销毁队列函数
	if (Q->data != NULL)
		free(Q->data);
}

void ClearQueue(SqQueue *Q) {
	//这里其实只需要把指针重新归位就好了,其他的数据会被后来的数据覆盖掉
	Q->rear = Q->front;
}

int QueueEmpty(SqQueue Q) { //是否为空,为空返回,非空返回0
	if (Q.front == Q.rear)
		return 1;
	else
		return 0;
}

int QueueFull(SqQueue Q) { //队列是否为满,已满返回1,未满返回0
	if ((Q.rear + 1) % MAXQSIZE == Q.front)
		return 1;
	else
		return 0;
}

int QueueLength(SqQueue Q) {//返回队列长度
	int count = 0;
	while (Q.front != Q.rear) {
		if (Q.front >= MAXQSIZE)
			Q.front -= MAXQSIZE;
		Q.front++;
		count++;
	}
	return count;
}

QElemType GetHead(SqQueue Q) { //若队列非空,返回队列首的值
	int e;
	if (QueueEmpty(Q) != 1) {
		e = Q.data[Q.front + 1];
		return e;
	} else
		printf("队列为空!\n");
}

void EnQueue(SqQueue *Q, QElemType e) { //将e元素插入队列
	if (QueueFull(*Q) == 1) {
		printf("队列已满,无法添加!\n");
		return;
	} else {
		Q->rear = (Q->rear + 1) % MAXQSIZE; //通过这个语句实现循环
		Q->data[Q->rear] = e;
	}
}

QElemType DeQueue(SqQueue *Q) { //删除队头元素,并返回其值
	QElemType e;
	if (QueueEmpty(*Q) != 1) {
		e = Q->data[Q->front + 1];
		Q->front = (Q->front + 1) % MAXQSIZE;
		//这里不用对移动后的front位置上的元素做处理,就当作没有,因为如果后续有也会被覆盖
		return e;
	} else {
		printf("队列为空!\n");
	}
}

void QueueTraverse(SqQueue Q) {
	if (QueueEmpty(Q) != 1) {
		Q.front++;
		while (Q.front != Q.rear) {
			printf("%c ", Q.data[Q.front]);
			Q.front++;
		}
		printf("%c", Q.data[Q.front]);
	} else
		printf("队列为空!\n");
}

测试输出:

(tip:这里的测试把前面两种队列作为头文件都可以使用,要自己把头文件更改,并且把第五行的Q类型进行更改,链队列是LinkQueue Q;循环队列是SqQueue Q,其他不变

#include "循环队列.h"
//如果是链队列,要更改上面一行的头文件,调用第一个部分的代码
main() {
	printf("测试初始化一个队列:\n");
//如果头文件用的是链队列,就把下面一行改成LinkQueue Q
	SqQueue Q;
	if (InitQueue(&Q) == 1)
		printf("初始化成功!\n");
	QElemType e, ch[15], ch1[15];
	puts("请输入要放入队列中的字符串:");
	scanf("%s", ch);
	for (int i = 0; i < strlen(ch); i++) {
		printf("存入第%d个字符:%c\n", i + 1, ch[i]);
		EnQueue(&Q, ch[i]);
	}
	//测试从队列头到尾遍历队列元素并输出
	printf("测试从队列中访问元素:\n");
	QElemType e1;
	e1 = GetHead(Q);
	printf("队列头的元素是:%c\n", e1);
	printf("队列的长度为: %d\n", QueueLength(Q));
	printf("请确认您的输入:");
	QueueTraverse(Q);
	printf("\n");
	printf("测试逐个弹出队列元素:\n");
	QElemType  a;//储存每次弹出的元素
	while (QueueEmpty(Q) != 1) { //如果栈非空
		a = DeQueue(&Q);
		printf("%c ", a);
	}
	printf("\n");
	//测试ClearQueue(Q)清空队列操作
	puts("重新为队列赋值:");
	puts("请输入要放入队列中的字符串:");
	scanf("%s", ch1);
	for (int i = 0; i < strlen(ch1); i++) {
		printf("存入第%d个字符:%c\n", i + 1, ch1[i]);
		EnQueue(&Q, ch1[i]);
	}
	printf("请确认您的输入:");
	QueueTraverse(Q);
	printf("\n");
	ClearQueue(&Q);
	printf("执行后结果:\n");
	QueueTraverse(Q);
	//测试销毁
	DestroyQueue(&Q);
	printf("done!");
}

结果参考:

 初学小白,有错误欢迎指正喔!!

  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

想写好代码的小猫头

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

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

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

打赏作者

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

抵扣说明:

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

余额充值