c语言实例 -- 循环链表

要求:

创建一个循环链表

下面是一个简单的循环链表(循环单链表)的C语言实现。循环链表是指链表的最后一个节点的指针指向第一个节点,形成一个环形结构。这种结构的优点是在一些特定的场景可以简化操作逻辑。

循环链表的基本操作

  1. 初始化循环链表
  2. 插入节点
  3. 删除节点
  4. 打印链表
  5. 销毁链表
c 复制代码
#include <stdio.h>
#include <stdlib.h>

// 定义节点结构
typedef struct Node {
    int data;
    struct Node* next;
} Node;

// 初始化循环链表
Node* initialize() {
    Node* head = NULL;
    return head;
}

// 插入节点
void insert(Node** head, int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    if (*head == NULL) {
        // 如果链表为空,新节点即为头节点
        *head = newNode;
        newNode->next = newNode;
    } else {
        Node* temp = *head;
        // 找到链表的最后一个节点
        while (temp->next != *head) {
            temp = temp->next;
        }
        temp->next = newNode; // 最后节点指向新节点
        newNode->next = *head; // 新节点指向头节点
    }
}

// 删除节点
void delete(Node** head, int key) {
    if (*head == NULL) return; // 如果链表为空,不做处理

    Node *temp = *head, *prev;

    // 如果头节点就是需要删除的节点
    if (temp->data == key) {
        // 找到最后一个节点,重新设置其next指针
        Node* last = *head;
        while (last->next != *head) {
            last = last->next;
        }
        if (*head == last) {
            free(*head);
            *head = NULL;
        } else {
            last->next = temp->next;
            *head = temp->next;
            free(temp);
        }
        return;
    }

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

    // 如果找到了,删除节点
    if (temp->data == key) {
        prev->next = temp->next;
        free(temp);
    }
}

// 打印链表
void printList(Node* head) {
    if (head == NULL) return;
    Node* temp = head;
    do {
        printf("%d ", temp->data);
        temp = temp->next;
    } while (temp != head);
    printf("\n");
}

// 销毁链表
void destroyList(Node** head) {
    if (*head == NULL) return;

    Node* temp = *head;
    Node* next;

    // 先处理第二个节点开始的部分
    while (temp->next != *head) {
        next = temp->next;
        free(temp);
        temp = next;
    }
    // 最后处理头节点
    free(temp);
    *head = NULL;
}

// 主函数
int main() {
    Node* head = initialize();

    insert(&head, 10);
    insert(&head, 20);
    insert(&head, 30);

    printf("Circular Linked List: ");
    printList(head);

    delete(&head, 20);
    printf("After deletion of 20: ");
    printList(head);

    destroyList(&head);
    return 0;
}

讲解

  • 初始化循环链表 :创建一个头节点的指针并将其初始化为NULL
  • 插入节点 :向链表中插入新节点时,首先创建新节点并将数据填充。如果链表为空,则新节点的next指针指向自己;否则,找到最后一个节点并调整指针以连接新节点,使其形成环。
  • 删除节点 :找到要删除的节点,如果是头节点,特别处理从temp->next开始的链表,调整最后一个节点的next,或者直接删除节点并重置头节点。如果不是头节点,则找到节点前的节点以调整其next指针。
  • 打印链表:从头节点开始,遍历链表直到回到头节点。
  • 销毁链表:释放所有节点的内存,循环释放直到再次回到头节点。

通过上面的代码,可以实现一个简单的循环链表,进行增删节点、遍历以及内存释放等基本操作。

情景设定:任务调度器

在很多嵌入式系统和实时操作系统中,经常需要多任务调度。例如,我们可以设想一个简单的定时任务调度器,在有限的硬件资源下循环顺序执行一组任务。我们可以使用循环链表来实现这种任务调度器,每个节点都表示一个待执行的任务。

每个任务执行后,将立即转移到下一个任务,形成一套循环的任务执行机制。这种设计能够保证所有任务被公平、持续地执行。

实现代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // 用于sleep函数

// 定义节点结构表示一个任务
typedef struct TaskNode {
    char* taskName;
    void (*execute)(void); // 指向任务执行函数的指针
    struct TaskNode* next;
} TaskNode;

// 初始化任务调度器
TaskNode* initializeScheduler() {
    return NULL;
}

// 添加任务
void addTask(TaskNode** head, char* taskName, void (*execute)(void)) {
    TaskNode* newTask = (TaskNode*)malloc(sizeof(TaskNode));
    newTask->taskName = taskName;
    newTask->execute = execute;

    if (*head == NULL) {
        *head = newTask;
        newTask->next = newTask;
    } else {
        TaskNode* temp = *head;
        while (temp->next != *head) {
            temp = temp->next;
        }
        temp->next = newTask;
        newTask->next = *head;
    }
}

// 删除任务
void deleteTask(TaskNode** head, char* taskName) {
    if (*head == NULL) return;

    TaskNode *current = *head, *prev = NULL;

    do {
        if (strcmp(current->taskName, taskName) == 0) {
            if (prev == NULL) {
                TaskNode* last = *head;
                while (last->next != *head) {
                    last = last->next;
                }
                if (current == last) {
                    free(current);
                    *head = NULL;
                } else {
                    last->next = current->next;
                    *head = current->next;
                    free(current);
                }
            } else {
                prev->next = current->next;
                free(current);
            }
            return;
        }
        prev = current;
        current = current->next;
    } while (current != *head);
}

// 任务调度循环
void runScheduler(TaskNode* head) {
    TaskNode* current = head;
    if (current == NULL) return;

    do {
        printf("Executing task: %s\n", current->taskName);
        current->execute();
        current = current->next;
        sleep(1); // 模拟任务切换间隔
    } while (current != head);
}

// 示例任务函数
void taskA() {
    printf("Task A is running.\n");
}

void taskB() {
    printf("Task B is running.\n");
}

void taskC() {
    printf("Task C is running.\n");
}

// 主函数
int main() {
    TaskNode* scheduler = initializeScheduler();

    addTask(&scheduler, "Task A", taskA);
    addTask(&scheduler, "Task B", taskB);
    addTask(&scheduler, "Task C", taskC);

    printf("Task Scheduler started\n");
    runScheduler(scheduler);

    deleteTask(&scheduler, "Task B");
    printf("\nTask B removed\n");

    runScheduler(scheduler);

    return 0;
}

讲解

  • 任务节点:每个节点存放一个任务的名称和其对应的执行函数。

  • 任务调度器的初始化:通过初始化函数创建一个空的任务链表,准备好后续任务的添加。

  • 添加任务:通过函数指针将具体任务的行为存储到节点中,并链接形成循环链表。

  • 删除任务:从链表中找到要删除的任务节点并释放内存,把链表重新链接。

  • 任务调度 :通过runScheduler函数循环调用每个任务对应的函数,模拟任务被连续调度执行的过程。

此示例实现了一个简单的任务调度器,通过循环链表结构,可以公平地调度任务,并支持任务的增删。这在嵌入式系统中可以用于管理有限的周期性任务执行。

相关推荐
Scott9999HH12 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社13 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海13 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖14 小时前
JDK 26 新特性详解
java·开发语言
马优晨14 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区16 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大17 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai17 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜17 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa17 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio