C语言中的循环链表

循环链表(Circular Linked List)是一种特殊的链表形式,它的最后一个节点的指针不是指向 NULL,而是指向链表的头节点,这样链表形成一个环,可以从任意节点开始进行遍历。在循环链表中,链表的头节点和尾节点通过指针连接,因此可以循环地访问链表中的所有节点。

循环链表既可以是 单向循环链表,也可以是 双向循环链表,但单向循环链表更常见。

循环链表的概念

  • 节点 (Node):链表的基本单元,包含数据和指针。
  • 头节点 (Head):链表的第一个节点。
  • 尾节点 (Tail):链表的最后一个节点,它的指针指向头节点,而不是 NULL
  • 循环特性:尾节点的指针指向头节点,使链表形成一个循环。
  • 单向循环链表:节点只包含一个指向下一个节点的指针。
  • 双向循环链表:节点包含指向前一个和下一个节点的两个指针。

循环链表的基本操作

  • 创建链表:初始化一个空的循环链表。
  • 插入节点:在链表头部、尾部或指定位置插入节点。
  • 删除节点:删除指定的节点。
  • 遍历链表:由于链表是循环的,遍历过程中需要注意避免无限循环。
  • 释放链表:释放链表中动态分配的所有节点内存。

单向循环链表的结构定义

#include <stdio.h>
#include <stdlib.h>

// 定义单向循环链表的节点结构
struct Node {
    int data;           // 数据域
    struct Node* next;  // 指针域,指向下一个节点
};

基本操作实现

  1. 创建新节点
// 创建新节点
struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));  // 分配内存
    if (!newNode) {
        printf("内存分配失败\n");
        exit(1);
    }
    newNode->data = data;  // 设置节点数据
    newNode->next = newNode;  // 初始化时,节点指向自身形成循环
    return newNode;
}
  1. 插入节点到链表头部
// 插入节点到链表头部
void insertAtHead(struct Node** head, int data) {
    struct Node* newNode = createNode(data);  // 创建新节点

    if (*head == NULL) {  // 如果链表为空
        *head = newNode;  // 新节点就是头节点
        newNode->next = newNode;  // 新节点指向自身,形成循环
        return;
    } 
        struct Node* temp = *head;

        // 找到链表的最后一个节点
        while (temp->next != *head) {
            temp = temp->next;
        }

        // 插入新节点到头部
        temp->next = newNode;  // 尾节点指向新节点
        newNode->next = *head;  // 新节点指向原头节点
        *head = newNode;        // 更新头节点

}
  1. 在链表尾部插入节点
// 插入节点到链表尾部
void insertAtTail(struct Node** head, int data) {
    struct Node* newNode = createNode(data);  // 创建新节点

    if (*head == NULL) {  // 如果链表为空
        *head = newNode;  // 新节点就是头节点
        newNode->next = newNode;  // 新节点指向自身,形成循环
    } else {
        struct Node* temp = *head;

        // 找到链表的最后一个节点
        while (temp->next != *head) {
            temp = temp->next;
        }

        // 插入新节点到尾部
        temp->next = newNode;  // 尾节点指向新节点
        newNode->next = *head;  // 新节点指向头节点,形成循环
    }
}
  1. 删除节点
// 删除值为 data 的节点
void deleteNode(struct Node** head, int data) {
    if (*head == NULL) return;  // 空链表

    struct Node* temp = *head;
    struct Node* prev = NULL;

    // 如果头节点就是要删除的节点
    if ((*head)->data == data) {
        if ((*head)->next == *head) {  // 链表只有一个节点
            free(*head);
            *head = NULL;
            return;
        }

        // 找到链表的最后一个节点
        while (temp->next != *head) {
            temp = temp->next;
        }

        temp->next = (*head)->next;  // 最后一个节点指向头节点的下一个节点
        free(*head);  // 释放原头节点
        *head = temp->next;  // 更新头节点
        return;
    }

    // 找到要删除的节点
    prev = *head;
    temp = (*head)->next;
    while (temp != *head && temp->data != data) {
        prev = temp;
        temp = temp->next;
    }

    // 如果找到要删除的节点
    if (temp->data == data) {
        prev->next = temp->next;
        free(temp);
    }
}
  1. 遍历链表
// 遍历链表并打印每个节点的数据
void printList(struct Node* head) {
    if (head == NULL) {
        printf("链表为空\n");
        return;
    }

    struct Node* temp = head;
    do {
        printf("%d -> ", temp->data);
        temp = temp->next;
    } while (temp != head);  // 循环直到再次回到头节点
    printf("(回到头节点)\n");
}
  1. 释放链表
// 释放链表的所有节点
void freeList(struct Node** head) {
    if (*head == NULL) return;

    struct Node* current = *head;
    struct Node* nextNode;

    do {
        nextNode = current->next;  // 保存下一个节点
        free(current);             // 释放当前节点
        current = nextNode;        // 移动到下一个节点
    } while (current != *head);    // 循环直到再次回到头节点

    *head = NULL;  // 头指针置为 NULL,表示链表已空
}

使用示例

int main() {
    struct Node* head = NULL;  // 初始化空链表

    // 插入数据
    insertAtHead(&head, 3);
    insertAtHead(&head, 2);
    insertAtHead(&head, 1);
    insertAtTail(&head, 4);
    insertAtTail(&head, 5);

    // 打印链表
    printf("循环链表内容: ");
    printList(head);

    // 删除节点
    deleteNode(&head, 3);
    printf("删除节点3后的链表: ");
    printList(head);

    // 释放链表
    freeList(&head);

    return 0;
}

运行结果

循环链表内容: 1 -> 2 -> 3 -> 4 -> 5 -> (回到头节点)
删除节点3后的链表: 1 -> 2 -> 4 -> 5 -> (回到头节点)

总结

  • 循环链表 与普通链表的主要区别在于其循环特性:尾节点指向头节点,形成一个循环结构。
  • 这种结构允许从任意节点开始遍历,并且能够简化某些需要循环操作的算法。
  • 与单向链表相比,循环链表在处理链尾时更加方便(不需要额外处理尾节点的 next 指针为 NULL 的情况)。
暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇