单向循环链表详解:C语言实现与应用
单向循环链表详解:C语言实现与应用
引言
单向循环链表是一种特殊的链表结构,其中最后一个节点指向第一个节点,形成一个闭环。这种数据结构在需要循环访问元素的场景中非常有用,如轮询调度、游戏循环等。本文将详细讲解如何使用C语言实现单向循环链表,包括各种基本操作的实现原理和代码分析。
数据结构定义
typedef int TypData;
typedef struct SingleList {
TypData data; // 数据域
struct SingleList *next; // 指向下一个节点的指针
} SList;
基本操作函数
1. 创建头节点
SList *CreHeadNode() {
SList *Head = (SList *)calloc(1, sizeof(SList));
if (NULL == Head) {
perror("创建头节点失败");
exit(EXIT_FAILURE);
}
Head->next = NULL; // 初始为空链表
return Head;
}
功能说明:创建链表的头节点,作为链表的入口点。头节点不存储实际数据,其next指针指向链表的第一个实际节点(首元节点)。
2. 创建新节点
SList *CreaOneNode(TypData data) {
SList *Node = (SList *)calloc(1, sizeof(SList));
if (NULL == Node) {
perror("创建节点失败");
exit(EXIT_FAILURE);
}
Node->data = data;
Node->next = NULL;
return Node;
}
功能说明:创建包含指定数据的新节点,初始化next指针为NULL。
3. 判断链表是否为空
bool IsEmpty(SList *List) {
return (List->next == NULL);
}
功能说明:检查链表是否为空(即头节点的next指针是否为NULL)。
4. 头插法插入节点
bool InstHeadNode(SList *Head, TypData data) {
SList *New = CreaOneNode(data);
if (NULL == New) return false;
if (IsEmpty(Head)) { // 空链表情况
New->next = New; // 自环
Head->next = New;
} else {
// 定位尾节点
SList *tail = Head->next;
while (tail->next != Head->next) {
tail = tail->next;
}
// 插入新节点并更新环
New->next = Head->next;
Head->next = New;
tail->next = New; // 尾节点指向新首节点
}
return true;
}
功能说明:
- 在链表头部插入新节点
- 空链表时创建自环
- 非空链表时:
- 新节点指向原首节点
- 头节点指向新节点
- 尾节点指向新节点(维护循环结构)
时间复杂度:O(n),需要遍历找到尾节点
5. 尾插法插入节点
bool InstTailNode(SList *Head, TypData data) {
if (IsEmpty(Head)) {
return InstHeadNode(Head, data); // 复用头插法
}
SList *New = CreaOneNode(data);
if (NULL == New) return false;
// 定位尾节点
SList *tail = Head->next;
while (tail->next != Head->next) {
tail = tail->next;
}
// 插入新节点
New->next = Head->next; // 新尾节点指向首节点
tail->next = New; // 原尾节点指向新节点
return true;
}
功能说明:
- 在链表尾部插入新节点
- 空链表时复用头插法
- 非空链表时:
- 新节点指向首节点(形成环)
- 原尾节点指向新节点
时间复杂度:O(n),需要遍历找到尾节点
6. 在指定数据后插入节点
bool InstDataNode(SList *Head, TypData NewData, TypData targetData) {
if (IsEmpty(Head)) {
printf("链表为空,插入失败\n");
return false;
}
SList *p = Head->next;
do {
if (p->data == targetData) {
SList *New = CreaOneNode(NewData);
if (NULL == New) return false;
New->next = p->next;
p->next = New;
return true;
}
p = p->next;
} while (p != Head->next);
printf("未找到数据 %d,插入失败\n", targetData);
return false;
}
功能说明:
- 在包含指定数据(targetData)的节点后插入新节点
- 使用do-while循环遍历整个链表
- 找到目标节点后插入新节点
时间复杂度:O(n)
7. 遍历并打印链表
void ListPrint(SList *Head) {
if (IsEmpty(Head)) {
printf("链表为空\n");
return;
}
SList *p = Head->next;
printf("链表内容: ");
do {
printf("%d -> ", p->data);
p = p->next;
} while (p != Head->next);
printf("[循环回到%d]\n", Head->next->data);
}
功能说明:
- 使用do-while循环遍历链表
- 打印每个节点的数据
- 显示循环回到的首节点数据
8. 删除头节点
bool DelHeadNode(SList *Head) {
if (IsEmpty(Head)) {
printf("链表为空,删除失败\n");
return false;
}
SList *delNode = Head->next;
if (delNode->next == delNode) { // 单节点情况
Head->next = NULL;
} else {
// 定位尾节点
SList *tail = Head->next;
while (tail->next != Head->next) {
tail = tail->next;
}
// 更新指针
Head->next = delNode->next;
tail->next = delNode->next;
}
free(delNode);
return true;
}
功能说明:
- 删除链表的第一个节点
- 处理单节点情况:直接置空头节点指针
- 多节点情况:
- 定位尾节点
- 更新头节点和尾节点的指针
- 释放被删除节点
时间复杂度:O(n),需要遍历找到尾节点
9. 删除尾节点
bool DelTailNode(SList *Head) {
if (IsEmpty(Head)) {
printf("链表为空,删除失败\n");
return false;
}
// 单节点情况
if (Head->next->next == Head->next) {
free(Head->next);
Head->next = NULL;
return true;
}
// 定位尾节点及其前驱
SList *prev = Head->next;
SList *curr = prev->next;
while (curr->next != Head->next) {
prev = curr;
curr = curr->next;
}
prev->next = Head->next; // 前驱节点指向首节点
free(curr);
return true;
}
功能说明:
- 删除链表的最后一个节点
- 单节点情况:直接删除并置空
- 多节点情况:
- 定位尾节点(curr)及其前驱节点(prev)
- 前驱节点指向首节点
- 释放尾节点
时间复杂度:O(n)
10. 删除指定数据的节点
bool DelSpecNode(SList *Head, TypData data) {
if (IsEmpty(Head)) {
printf("链表为空,删除失败\n");
return false;
}
SList *prev = NULL; // 前驱节点
SList *curr = Head->next; // 当前节点
do {
if (curr->data == data) {
if (curr == Head->next) { // 目标为首节点
return DelHeadNode(Head);
} else {
prev->next = curr->next;
free(curr);
return true;
}
}
prev = curr;
curr = curr->next;
} while (curr != Head->next);
printf("未找到数据 %d,删除失败\n", data);
return false;
}
功能说明:
- 删除包含指定数据(data)的节点
- 首节点情况:复用DelHeadNode()
- 其他节点:
- 更新前驱节点的指针
- 释放当前节点
- 遍历整个链表查找目标节点
时间复杂度:O(n)
11. 释放整个链表
void FreeList(SList *Head) {
if (IsEmpty(Head)) {
free(Head);
return;
}
// 先断开循环
SList *tail = Head->next;
while (tail->next != Head->next) {
tail = tail->next;
}
tail->next = NULL; // 断开循环
// 释放所有数据节点
SList *current = Head->next;
while (current != NULL) {
SList *temp = current;
current = current->next;
free(temp);
}
// 释放头节点
free(Head);
}
功能说明:
- 安全释放整个链表占用的内存
- 关键步骤:
- 先断开循环(避免死循环)
- 释放所有数据节点
- 最后释放头节点
单向循环链表的特点
- 循环结构:最后一个节点的next指针指向第一个节点
- 无NULL终止:遍历时需要特殊终止条件
- 高效循环访问:适合需要重复遍历的场景
- 插入/删除复杂度:
- 头插/头删:O(n)(需要找到尾节点)
- 尾插/尾删:O(n)
- 指定位置操作:O(n)
应用场景
- 轮询调度算法
- 多人游戏中的玩家轮流机制
- 循环缓冲区实现
- 约瑟夫环问题
- 周期性任务调度
完整测试示例
int main() {
// 创建链表
SList *list = CreHeadNode();
// 插入操作
InstHeadNode(list, 30); // 头部插入30
InstTailNode(list, 40); // 尾部插入40
InstHeadNode(list, 20); // 头部插入20
InstDataNode(list, 25, 20); // 在20后插入25
InstTailNode(list, 50); // 尾部插入50
ListPrint(list); // 输出: 20 -> 25 -> 30 -> 40 -> 50 -> [循环回到20]
// 删除操作
DelHeadNode(list); // 删除头节点(20)
ListPrint(list); // 输出: 25 -> 30 -> 40 -> 50 -> [循环回到25]
DelTailNode(list); // 删除尾节点(50)
ListPrint(list); // 输出: 25 -> 30 -> 40 -> [循环回到25]
DelSpecNode(list, 30); // 删除节点30
ListPrint(list); // 输出: 25 -> 40 -> [循环回到25]
// 释放链表
FreeList(list);
return 0;
}
总结
单向循环链表是一种重要的数据结构,特别适合需要循环访问元素的场景。本文详细讲解了C语言实现单向循环链表的各个关键操作,包括:
- 链表的基本结构和节点定义
- 各种插入操作(头插、尾插、指定位置插)
- 各种删除操作(头删、尾删、指定位置删)
- 链表遍历和内存释放
- 边界条件处理(空链表、单节点链表等)
通过合理使用单向循环链表,可以高效解决许多实际问题,如轮询调度、循环任务处理等。在实际应用中,需要特别注意循环结构的维护,确保在任何操作后链表都能保持正确的环状结构。

浙公网安备 33010602011771号