C语言——模拟实现队列

先讲一下队列的性质,向队列中存储数据,先存储进去的先取出来,后存储进去的后取出来,跟栈相反,就是“先进先出,后进后出”。

那么,在顺序表和单链表二者中使用哪一种更好呢?

如果使用顺序表,当要取出数据时,需要将首元素后面的元素都往前移,时间复杂度O(N),而单链表弥补了这个缺点,因此,模拟实现队列使用单链表更合适,为了使添加数据更方便,队列中包含了第一个元素的指针和最后一个元素的指针。

假设队列中存储的数据时int类型:

typedef int QDataType;

一个结点的结构体包含的内容有它的数据和指向下一个结点的指针,将它重命名为QNode。

typedef struct QNode
{
	QDataType data;
	struct QNode* next;
}QNode;

队列中包含第一个结点的指针和最后一个结点的指针。

typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

在头文件中声明要实现功能的函数,这里讲解队列的初始化、销毁、入队列、出队列、计算队列中有效数据数量、取队列第一个和最后一个元素以及判断队列是否为空。

//声明函数

//初始化队列
void InitQueue(Queue* ps);

//销毁队列
void DestoryQueue(Queue* ps);

//添加数据到队列
void QueuePush(Queue* ps, QDataType x);

//删除队列的第一个数据
void QueuePop(Queue* ps);

//取队列第一个数据
QDataType QueueFront(Queue* ps);

//取队列最后一个数据
QDataType QueueBack(Queue* ps);

//计算队列中数据个数
int QueueSize(Queue* ps);

//判断队列是否为空
bool QueueEmpty(Queue* ps);

1.初始化队列

//初始化队列
void InitQueue(Queue* ps)
{
	assert(ps);

	ps->head = ps->tail = NULL;
}

队列中包含第一个元素的指针和最后一个元素的指针,显而易见,初始化队列就是将这两个指针置为空即可。

2.销毁队列

//销毁队列
void DestoryQueue(Queue* ps)
{
	assert(ps);

	QNode* cur = ps->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	ps->head = ps->tail = NULL;
}

销毁队列不能只将队列中的两个指针置为空,首先要将存在的元素全部free掉,之后才能将头指针的尾指针置为空。

3.入队列

//添加数据到队列
void QueuePush(Queue* ps, QDataType x)
{
	assert(ps);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = NULL;

	if (ps->head == NULL)
	{
		ps->head = ps->tail = newnode;
	}
	else
	{
		ps->tail->next = newnode;
		ps->tail = newnode;
	}
}

入数据到队列是将这个数据放到队列的最后,先开辟一个新的结点,初始化它,这时候要分类讨论,如果队列中一个元素都没有,就将头指针和尾指针都指向这个新的节点,存在元素就将新结点放到尾结点后面,再将新结点的地址赋给尾指针即可。

4.出队列

//删除队列的第一个数据
void QueuePop(Queue* ps)
{
	assert(ps);
	assert(ps->head);

    
	if (ps->head->next == NULL)
	{
		ps->head = ps->tail = NULL;
	}
	else
	{
		QNode* NewHead = ps->head->next;
		free(ps->head);
		ps->head = NewHead;
	}
}

从队列出数据是将队列中的一个元素取出后删除它,所以要断言队列不能为空,为空的话就没有元素可以删。队列中有元素就新建一个指针记住头结点的下一个结点的地址,free掉头结点,再将新建结点指针的地址赋给头结点即可。要注意,如果只有一个结点的话,如果不作分类讨论,将ps->head给free掉后,ps->tail就会变成野指针,这一点很关键。

5.取队列第一个数据

//取队列第一个数据
QDataType QueueFront(Queue* ps)
{
	assert(ps);
	assert(ps->head);

	return ps->head->data;
}

得到头结点的数据很简单,先要断言头结点不能为空,直接return头结点的data就行了。

6.取队列最后一个数据

//取队列最后一个数据
QDataType QueueBack(Queue* ps)
{
	assert(ps);
	assert(ps->tail);
	
	return ps->tail->data;
}

跟取队列第一个元素一样,先断言,然后直接return即可。

7.计算队列中有效数据个数

//计算队列中数据个数
int QueueSize(Queue* ps)
{
	assert(ps);

	int count = 0;
	QNode* cur = ps->head;
	while (cur)
	{
		count++;
		cur = cur->next;
	}

	return count;
}

创建int变量count记住数量,遍历链表后,返回count就行了。

8.判断队列是否为空

//判断队列是否为空
bool QueueEmpty(Queue* ps)
{
	assert(ps);

	return ps->head == NULL;
}

如果头结点为空,表示没有元素,返回true,反之就返回false。

接下来测试以下

void TestQueue()
{
	Queue q;

	InitQueue(&q);

	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");

	DestoryQueue(&q);

}

将“1,2,3,4”入队列后,再打印,方式与栈相同,如果队列不为空,打印第一个元素,并删除它,直到队列为空。

void TestQueue()
{
	Queue q;

	InitQueue(&q);

	QueuePush(&q, 1);
	printf("%d\n", QueueFront(&q));
	QueuePop(&q);

	QueuePush(&q, 2);
	printf("%d\n", QueueFront(&q));
	QueuePop(&q);

	QueuePush(&q, 3);
	QueuePush(&q, 4);
	
	printf("%d\n", QueueSize(&q));

	DestoryQueue(&q);

}

 入队列一个打印一个也是可以的,将“1”入队列打印并删除,入“2”打印并删除,将“3,4”如果队列后计算队列的有效数据个数:

以上就是队列的基本用法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值