单链表队列
队列是计算机科学中基础且核心的数据结构,遵循 “先进先出”(FIFO, First In First Out) 原则 —— 类比日常生活中的排队:先排队的人先办事,后排队的人只能在队尾等待,队列的操作严格遵循这一逻辑,是解决 “顺序处理” 问题的关键工具。
其核心是最早入队(enqueue)的元素,必定最早出队(dequeue),中间元素无法直接访问或删除(仅允许操作队首和队尾)。
相关概念:
| 队首(Front) | 队列中第一个元素,是出队操作的唯一入口。 |
| 队尾(Rear/Tail) | 队列中最后一个元素,是入队操作的唯一入口。 |
| 入队(Enqueue) | 向队尾添加新元素的操作。 |
| 出队(Dequeue) | 从队首删除并返回元素的操作。 |
| 判空(IsEmpty) | 检查队列是否无元素(队首 = 队尾,或长度为 0)。 |
| 判满(IsFull) | 仅固定大小的队列需要(如数组实现),检查队列是否达到最大容量。 |
| 查看队首(Peek/Front) | 返回队首元素但不删除(与出队的核心区别) |

1.链表队列
用单链表实现链式队列。

在上面结构的插入和删除数据有两种选择:
1.头部插O(1) 尾部删O(n)
2.头部删O(1) 尾部插O(n)
发现上述的单链表的设计都打到我们的需求,需要对其进行改造:
对辅助节点进行改造,让其多一个指针域,用来指向尾结点

在上面结构的插入和删除数据有两种选择:
1.头部插O(1) 尾部删O(n)
2.头部删O(1) 尾部插O(1)
我们则使用第二个情况,使得时间复杂度为O(1)
既然我们发现辅助节点需要两个指针域,而有效节点是一个数据域一个指针域,这时辅助节点就只能单独设计了,则既然要单独设计,则可以将哪个没有的数据域不要了。

0.5结构体设计
//链式队列的有效节点的结构体设计:
typedef struct LQueue_Node
{ELEMTYPE data;//数据域LQueue_Node* next;//指针域,指向下一个有效节点
}LQueue_Node;//链式队列的辅助节点的结构体设计:
typedef struct Link_Queue
{LQueue_Node* front;//队头指针:用来指向第一个有效节点LQueue_Node* rear;//队尾指针:用来指向最后一个有效节点
}Link_Queue;
1.初始化
对于初始化我们只需将next域和rear域置空就行。
void Init_LinkQueue(Link_Queue* plist)
{assert(plist != NULL);plist->front = NULL;plist->rear = NULL;
}
1.5购买新节点
struct LQueue_Node* BuyNode(int val)
{struct LQueue_Node* p = (struct LQueue_Node*)malloc(sizeof(struct LQueue_Node));if (p == NULL)return NULL;p->next = NULL;p->data = val;return p;
}
2.入队(尾部入)


我们在讨论入队的情况的时候,有两种情况
1.队列为空,需要修改两个参数 front rear
2.队列不为空,需要修改两个参数 rear 最后一个节点的next域
bool Push(Link_Queue* plist, ELEMTYPE val)
{assert(plist != NULL);LQueue_Node* p = BuyNode(val);if (p == NULL)return false;if (Empty(plist)) {plist->front = p;plist->rear = p;p->next = NULL;}else{plist->rear->next = p;plist->rear = p;}return true;
}
3.出队(头部出)

出队有三种种情况
1.链表无节点 直接return false
2.链表有一个节点 free(front),修改 rear域 front域 为NULL
3.链表有多个节点 free(front),修改front域为第二个结点
bool Pop(Link_Queue* plist)
{assert(plist != NULL);if (Empty(plist))return false;LQueue_Node* q = plist->front;if (plist->front == plist->rear){free(q);q = NULL;plist->front = NULL;plist->rear = NULL;}else{plist->front = q->next;free(q);q = NULL;}return true;
}
4. 获取栈头元素值
ELEMTYPE Front(Link_Queue* plist)
{if (Empty(plist))return -1;return plist->front->data;
}
5.判空
bool Empty(Link_Queue* plist)
{assert(plist != NULL);return plist->front == NULL;
}
6.销毁
在我们遇见销毁有一个固定的步骤:
1.判断链表是否为空2.不为空if(!empty)直接头删即出队
3.front和rear置为NULL
void Destroy(Link_Queue* plist)
{assert(plist != NULL);if (!Empty(plist)){Pop(plist);}plist->front = NULL;plist->rear = NULL;
}
7.打印
void Show(Link_Queue* plist)
{assert(plist != NULL);if (Empty(plist))return;LQueue_Node* p = plist->front;for (; p != NULL; p = p->next)printf("%d", p->data);printf("\n");
}
