循环链表(Circular Linked List)是链表的一种形式,其特点是链表中最后一个节点指向第一个节点,形成一个环形结构。本文将详细介绍在C语言中实现循环链表的操作,包括常见的操作及其时间复杂度分析。
#include <stdio.h>
#include <stdlib.h>
// 定义循环链表节点结构
typedef struct Node {
int data; // 数据域
struct Node *next; // 指向下一个节点的指针
} Node;
// 创建新节点
Node *createNode(int data) {
Node *newNode = (Node *)malloc(sizeof(Node));
if (newNode == NULL) {
printf("Memory allocation failed!\n");
return NULL;
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 头部插入新节点
void insertAtBeginning(Node **head, int data) {
Node *newNode = createNode(data);
if (*head == NULL) {
// 如果链表为空,则新节点指向自身形成环
*head = newNode;
newNode->next = *head;
} else {
// 找到链表尾节点,将尾节点的next指向新节点,新节点变成头节点
Node *temp = *head;
while (temp->next != *head) {
temp = temp->next;
}
newNode->next = *head;
*head = newNode;
temp->next = *head;
}
}
// 尾部插入新节点
void insertAtEnd(Node **head, int data) {
Node *newNode = createNode(data);
if (*head == NULL) {
// 如果链表为空,则新节点指向自身形成环
*head = newNode;
newNode->next = *head;
} else {
// 找到链表尾节点,将尾节点的next指向新节点,新节点成为新的尾节点
Node *temp = *head;
while (temp->next != *head) {
temp = temp->next;
}
temp->next = newNode;
newNode->next = *head;
}
}
// 删除指定值的节点
void deleteNode(Node **head, int key) {
if (*head == NULL) {
printf("List is empty!\n");
return;
}
Node *temp = *head, *prev = NULL;
while (temp->data != key) {
if (temp->next == *head) {
printf("Element %d not found in the list!\n", key);
return;
}
prev = temp;
temp = temp->next;
}
// 删除头节点的情况
if (temp == *head && temp->next == *head) {
*head = NULL;
free(temp);
return;
}
// 如果删除的是头节点
if (temp == *head) {
prev = *head;
while (prev->next != *head)
prev = prev->next;
*head = (*head)->next;
prev->next = *head;
} else {
// 一般情况,删除非头节点
prev->next = temp->next;
}
free(temp);
}
// 遍历循环链表并打印节点值
void traverse(Node *head) {
if (head == NULL) {
printf("List is empty!\n");
return;
}
Node *temp = head;
do {
printf("%d ", temp->data);
temp = temp->next;
} while (temp != head);
printf("\n");
}
// 主函数用于测试
int main() {
Node *head = NULL; // 初始化循环链表头指针
// 插入操作测试
insertAtEnd(&head, 1);
insertAtEnd(&head, 2);
insertAtBeginning(&head, 3);
// 输出当前链表
printf("Current list: ");
traverse(head); // 应输出 3 1 2
// 删除操作测试
deleteNode(&head, 1);
// 输出删除后的链表
printf("List after deletion: ");
traverse(head); // 应输出 3 2
return 0;
}
时间复杂度分析:
-
插入操作:
- 头部插入:O(1)
- 尾部插入:O(n)(因为要遍历至链表尾部)
-
删除操作:
- 删除节点:O(n)(因为可能需要遍历至链表尾部找到要删除的节点)
-
遍历操作:
- 遍历整个链表:O(n)
这些操作的时间复杂度主要受限于遍历链表的次数,因为循环链表的特性会导致在一些情况下需要遍历整个链表才能完成操作。