一.循环链表的定义
循环链表是一种链表的变体,它与单向链表相似,但是有一个重要的区别:循环链表的最后一个节点指向第一个节点,形成一个环状结构。循环链表是一种有用的数据结构,适用于需要从链表的任何节点开始遍历的场景。但是,它也比单向链表稍微复杂一些,需要更多的存储空间和更复杂的操作。
二.循环链表的特点
1.最后一个节点的指针指向第一个节点,形成一个闭环。
2.由于循环链表是一个循环的结构,可以在遍历时无限循环下去,不会出现尾部节点的空指针错误。
3.可以通过任意节点进行遍历,因为每个节点都有下一个节点的指针。
4.循环链表可以从任意节点开始遍历,因此可以选择一个合适的起始节点来提高操作效率。
三.循环列表的实现
1.初始化
#include <stdio.h>
#include <stdlib.h> // 为了使用malloc和free
// 定义节点结构
typedef struct Node {
int value;
struct Node* next_node;
} Node;
// 定义循环链表结构
typedef struct CircularList {
Node* start;
Node* end;
int size;
} CircularList;
// 初始化循环链表
void initializeList(CircularList* list) {
list->start = NULL;
list->end = NULL;
list->size = 0;
}
// 判断循环链表是否为空
int isEmpty(CircularList* list) {
return list->size == 0;
}
2.循环列表的插入
void insertAtBeginning(CircularList* list, int data) {
Node* new_node = (Node*)malloc(sizeof(Node));
new_node->value = data;
new_node->next_node = list->start;
list->start = new_node;
if (isEmpty(list)) {
list->end = new_node;
new_node->next_node = list->start;
}
list->size++;
}
// 在任意位置插入节点
void insertAtPosition(CircularList* list, int data, int position) {
Node* new_node = (Node*)malloc(sizeof(Node));
new_node->value = data;
if (isEmpty(list) || position <= 0) {
new_node->next_node = list->start;
list->start = new_node;
if (isEmpty(list)) {
list->end = new_node;
new_node->next_node = list->start;
}
} else {
Node* temp = list->start;
int count = 0;
while (temp != NULL && count < position - 1) {
temp = temp->next_node;
count++;
}
if (temp == NULL) {
new_node->next_node = list->start;
list->end->next_node = new_node;
list->end = new_node;
} else {
new_node->next_node = temp->next_node;
temp->next_node = new_node;
}
list->size++;
}
}
// 在尾部插入节点
void insertAtEnd(CircularList* list, int data) {
Node* new_node = (Node*)malloc(sizeof(Node));
new_node->value = data;
new_node->next_node = list->start;
if (isEmpty(list)) {
list->start = new_node;
list->end = new_node;
} else {
list->end->next_node = new_node;
list->end = new_node;
}
list->size++;
}
// 获取循环链表长度
int getListLength(CircularList* list) {
return list->size;
}
3.删除指定节点
void deleteNode(CircularList* list, int data) {
if (isEmpty(list)) {
printf("循环链表为空,无法删除。\n");
return;
}
Node* current = list->start;
Node* previous = NULL;
do {
if (current->value == data) {
if (current == list->start) {
list->start = current->next_node;
if (list->size == 1) {
list->end = NULL;
} else {
list->end->next_node = list->start;
}
} else {
previous->next_node = current->next_node;
if (current == list->end) {
list->end = previous;
}
}
free(current);
list->size--;
return;
}
previous = current;
current = current->next_node;
} while (current != list->start);
printf("未找到元素。\n");
}
4.查询指定的节点
int findPosition(CircularList* list, int data) {
if (isEmpty(list)) {
return -1;
}
Node* current = list->start;
int position = 0;
do {
if (current->value == data) {
return position;
}
current = current->next_node;
position++;
} while (current != list->start);
return -1;
}
5.打印循环链表
void printCircularList(CircularList* list) {
if (isEmpty(list)) {
printf("循环链表为空。\n");
return;
}
Node* node = list->start;
do {
printf("%d ", node->value);
node = node->next_node;
} while (node != list->start);
printf("\n");
}
五.运行结果
六.小结
循环链表是一种特殊的链表,其中最后一个元素指向第一个元素,形成循环。这种类型的链表在处理一些特定的数据结构和算法问题时非常有用。
- 节点结构:循环链表的每个节点通常包含两个基本字段:数据字段和指向下一个节点的指针字段。在循环链表中,最后一个节点的指针字段指向链表的第一个节点,形成一个闭环。
- 链表结构:循环链表由头节点和尾节点组成。头节点是链表的入口,尾节点指向链表的最后一个节点。与单链表不同,循环链表的尾节点的指针字段指向头节点,形成一个闭环。
- 初始化:初始化循环链表时,需要设置头节点和尾节点,并确保尾节点的指针字段指向头节点。
- 插入操作:在循环链表中的任何位置插入一个新节点,需要调整节点的指针字段以确保链表的连续性。特别是当在链表尾部插入新节点时,需要将新节点设置为新的尾节点并将原尾节点的指针字段指向新节点。
- 删除操作:从循环链表中删除一个节点时,需要调整节点的指针字段以移除该节点。特别是在删除头节点时,需要特别小心,确保链表的其余部分保持正确连接。
- 遍历操作:遍历循环链表时,通常从一个节点开始,沿着指针字段一直遍历到再次到达开始节点的下一个节点。由于循环链表是一个闭环,所以可以遍历整个链表多次。
- 特殊应用:循环链表特别适用于需要循环访问的场景,如创建循环缓冲区、实现环形振荡器或者在物理建模中模拟刚体的连接等。
- 注意事项:由于循环链表的最后一个元素指向第一个元素,因此在实现时应注意避免在删除或插入操作中形成环路,否则可能会导致无限循环等问题。