循环单链表是一种特殊的单向链表,其最后一个节点的指针不是指向空(NULL),而是指向链表的头节点,形成一个环状结构,如下图所示。这样一来,从任何节点出发都可以遍历整个链表。
在循环单链表中,需要特别注意以下几点:
- 初始化:创建循环单链表时,需要确保头节点的指针指向自身,即头节点的next指针指向头节点本身。
- 插入节点:插入节点时,要确保新节点被正确插入到链表中,并更新相邻节点的指针。
- 删除节点:删除节点时,也需要更新相邻节点的指针,确保链表结构仍然保持完整。
- 遍历链表:遍历循环单链表时,需要设置一个终止条件,以免陷入无限循环。
循环单链表的应用:
-
在栈和队列的实现中,可以使用循环单链表进行存储和管理
-
在树形结构的遍历中,可以使用其进行层级遍历
-
在图的遍历中,可以使用其进行深度优先搜索或广度优先搜索
循环单链表的优缺点:
-
可以解决单链表无法解决的问题,例如寻找倒数第i个节点等
-
缺点是实现相对复杂,需注意循环边界条件和指针的维护
-
其插入和删除的操作时间复杂度为O(n),比单链表高
示例代码如下:
#include <stdio.h>
#include <stdlib.h>
// 定义循环单链表的节点结构
typedef struct Node {
int data; // 数据域
struct Node* next; // 指针域
} Node;
// 初始化循环单链表
void initList(Node** head) {
*head = (Node*)malloc(sizeof(Node));
(*head)->next = *head;
}
// 插入元素到循环单链表
void insertNode(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = head->next;
head->next = newNode;
}
// 删除指定元素的节点
void deleteNode(Node* head, int value) {
Node* p = head;
while (p->next != head) {
if (p->next->data == value) {
Node* temp = p->next;
p->next = temp->next;
free(temp);
return;
}
p = p->next;
}
}
// 遍历循环单链表
void traverseList(Node* head) {
Node* p = head->next;
while (p != head) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
//根据值查找节点位置
int findNodePosition(Node* head, int target) {
// 检查链表是否为空
if (head == NULL) {
return -1;
}
// 定义一个指向链表头节点的指针
Node* current = head;
int position = 0;
// 遍历链表
do {
// 检查当前节点的数据是否等于目标数据
if (current->data == target) {
return position ; // 找到目标数据,返回当前节点的位置
}
// 移动到下一个节点
current = current->next;
position++;
} while (current != head); // 当回到头节点时停止循环
return -1; // 链表中无目标数据,返回-1
}
// 查找循环单链表第i个节点的数值
int findNodeValue(Node *head, int i) {
Node *current = head;
int position = 0;
do {
if (position == i) {
return current->data;
}
current = current->next;
position++;
} while (current != head);
// 未找到第i个节点
return -1;
}
/* 查找循环单链表中倒数第i个节点的数据
*
* 要查找循环单链表中倒数第i个节点的数据,可以使用"快慢指针"的方法来实现。具体步骤如下:
* a.定义两个指针,分别命名为fast和slow,初始时均指向链表的头节点。
* b.让fast指针先向前移动i步,然后让fast和slow指针一起向前移动,直到fast指针到达链表末尾(即指向头节点时停止)。
* c.此时slow指针所指向的节点即为倒数第i个节点。
*/
int findReverseNodeValue(Node *head, int i) {
Node *fast = head;
Node *slow = head;
int count = 0;
i -= 1;
// 让fast指针先向前移动i步
while (count < i && fast->next != head) {
fast = fast->next;
count++;
}
// 处理i超出链表长度的情况
if (count < i) {
return -1;
}
// 让fast和slow指针一起向前移动
while (fast->next != head) {
fast = fast->next;
slow = slow->next;
}
// 返回slow指针所指向的节点数据
return slow->data;
}
/* 删除循环单链表
* 要删除循环单链表,可以按照以下步骤进行操作:
* a.从头节点开始遍历整个循环单链表,依次释放每个节点的内存空间。
* b.最后将头节点指针置为NULL,表示链表已经被删除。
*/
void deleteCircularLinkedList(Node **head) {
Node *current = *head;
Node *next;
// 遍历链表,释放每个节点的内存空间
do {
next = current->next;
free(current);
current = next;
} while (current != *head);
// 将头节点指针置为NULL
*head = NULL;
}
int main() {
Node* head;
initList(&head);
// 插入元素
insertNode(head, 12);
insertNode(head, 13);
insertNode(head, 23);
insertNode(head, 43);
// 遍历链表
traverseList(head);
// 在循环单链表中查找数据对应的位置
int target = 13;
int position= findNodePosition(head, target);
if (position != -1) {
printf("节点值为 %d 的节点在链表中的位置为: %d\r\n", target, position);
} else {
printf("Target data not found.\n");
}
// 删除元素
deleteNode(head, 13);
printf("删除后遍历值:\r\n");
// 遍历链表
traverseList(head);
//根据位置查找对应节点的数据
int index = 2;
int value = findNodeValue(head, index );
if (value != -1) {
printf("第%d个节点的数值为: %d\n", index, value);
} else {
printf("未找到第%d个节点\n", index);
}
//查找循环单链表中倒数第i个节点的数据
int value2 = findReverseNodeValue(head, index);
if (value2 != -1) {
printf("倒数第%d个节点的数据为: %d\n", index, value2);
} else {
printf("未找到倒数第%d个节点\n", index);
}
// 删除循环单链表
deleteCircularLinkedList(&head);
// 检查链表是否已删除
if (head == NULL) {
printf("循环单链表已成功删除\n");
} else {
printf("循环单链表删除失败\n");
}
return 0;
}
该程序输出为:
如有错误,欢迎指出,共同学习共同进步~