【数据结构】C语言实现队列(链式队列)


一、队列

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)

入队列:进行插入操作的一端称为队尾

出队列:进行删除操作的一端称为队头


队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

链式队列:链表的头当做队头,链表的尾当做队尾,这样在尾部插入与在头部删除比较简单。


我们使用无头、单向、非循环链表来实现链式队列,因为队列需要频繁的队头和队尾操作,所以我们定义头指针和尾指针。

因为我们使用的是无头链表实现,所以插入时需要对空链表单独处理,删除时,需要对只有一个元素的情况单独处理。

二、链式队列的接口定义

结点定义:因为使用单链表实现队列,所以队列结点与单链表结点相同。

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

由于频繁对队头、队尾进行操作,所以有head和tail两个指针:

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

我们使用size变量记录队列中的元素个数,这样可以直接返回该值,但在插入和删除时要维护该size的值。如果不加size成员,就需要遍历链表,累计size:

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

三、链式队列的实现

3.1 初始化Init

void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

3.2 销毁Destroy

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

3.3 队列判空empty

可以通过头尾指针都为空来判断队列是否为空,也可以通过队列的元素size是否等于0来判断队列是否为空。

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	/*return pq->phead == NULL
		&& pq->ptail == NULL;*/
	return pq->size == 0;
}

3.4 入队push-EnQueue

队尾入队,也就是对链表的尾插。

单链表进行尾插时需要找到尾结点,由于这里我们使用ptail尾指针就是指向尾结点,因此找尾这一步省去了。

找到尾结点后需要插入到尾结点后,值得说的是,由于插入时单链表可能为空,因此需要对为空的情况单独处理:

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	// 待插入结点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail\n");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
		// 1.链表为空
		assert(pq->phead == NULL);

		pq->phead = pq->ptail = newnode;
	}
	else
	{
		// 2.链表不为空直接插入尾结点后
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

3.5 出队pop-DeQueue

队首出队,也就是链表的头删。

删除时需要判空处理,队列中至少有一个元素才能进行删除,我们可以使用断言实现。

对于没有尾指针的单链表头删非常简单,只需要将头指针指向头结点下一个结点。

但是我们使用单链表实现队列时,附加了尾指针,这时链表的头删还需要考虑尾指针,直白的说就是在删除最后一个元素时,还要修改尾指针的指向。

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	if (pq->phead->next == NULL)
	{
		// 删除最后一个元素时修改尾指针指向
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		// 正常头删
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}

	pq->size--;
}

3.6 队首front

头指针phead指向头结点,便找到链表第一个元素,也就是队首元素。

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

3.7 队尾back

尾指针指向尾结点,便找到链表最后一个元素,也就是队尾元素。

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}

源码

Gitee-Queue

总结

链式队列的实现本质上是对单链表进行操作。

入队和出队分别对应单链表的尾插和头删。因为是无头单向非循环的单链表,因此尾插时需要对空链表单独处理。又因为我们需要维护尾指针,因此头删时,删除最后一个元素时还需要对尾指针单独处理。这些在上面代码中都有所体现。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
下面是使用 C 语言实现队列的基本代码: ```c #include <stdio.h> #include <stdlib.h> // 链队列结构体定义 typedef struct QueueNode { int data; struct QueueNode* next; } QueueNode; typedef struct { QueueNode* front; // 队头指针 QueueNode* rear; // 队尾指针 } LinkQueue; // 初始化链队列 void InitQueue(LinkQueue* Q) { Q->front = Q->rear = (QueueNode*)malloc(sizeof(QueueNode)); Q->front->next = NULL; } // 入队操作 void EnQueue(LinkQueue* Q, int x) { QueueNode* s = (QueueNode*)malloc(sizeof(QueueNode)); s->data = x; s->next = NULL; Q->rear->next = s; Q->rear = s; } // 出队操作 int DeQueue(LinkQueue* Q) { if (Q->front == Q->rear) { printf("队列为空!\n"); exit(-1); } QueueNode* p = Q->front->next; int x = p->data; Q->front->next = p->next; if (Q->rear == p) Q->rear = Q->front; free(p); return x; } // 判断队列是否为空 int IsEmpty(LinkQueue* Q) { if (Q->front == Q->rear) return 1; else return 0; } // 获取队列元素个数 int GetLength(LinkQueue* Q) { int count = 0; QueueNode* p = Q->front->next; while (p) { count++; p = p->next; } return count; } // 清空队列 void ClearQueue(LinkQueue* Q) { QueueNode* p = Q->front->next; while (p) { QueueNode* temp = p; p = p->next; free(temp); } Q->front->next = NULL; Q->rear = Q->front; } // 输出队列元素 void PrintQueue(LinkQueue* Q) { QueueNode* p = Q->front->next; while (p) { printf("%d ", p->data); p = p->next; } printf("\n"); } int main() { LinkQueue Q; InitQueue(&Q); EnQueue(&Q, 1); EnQueue(&Q, 2); EnQueue(&Q, 3); EnQueue(&Q, 4); PrintQueue(&Q); printf("队列长度为:%d\n", GetLength(&Q)); printf("出队元素为:%d\n", DeQueue(&Q)); printf("出队元素为:%d\n", DeQueue(&Q)); PrintQueue(&Q); printf("队列长度为:%d\n", GetLength(&Q)); ClearQueue(&Q); printf("清空队列后,队列长度为:%d\n", GetLength(&Q)); return 0; } ``` 在这个代码中,使用了链表来存储队列元素。其中,`front` 指针指向链队列的头结点,`rear` 指针指向链队列的尾结点。新元素入队时,将其插入链表尾部,并修改 `rear` 指针的指向。元素出队时,从链表头部取出元素,并修改 `front` 指针的指向。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

shlyyy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值