linux下c语言中的单向列表,双向链表,内核双向列表,及适用场景

2024-09-05 23:12

本文主要是介绍linux下c语言中的单向列表,双向链表,内核双向列表,及适用场景,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 单向链表(Singly Linked List)

1.1 定义与结构
单向链表是链式存储结构中最简单的一种。它的每个节点包含两个部分:
- 数据域:存储数据元素
- 指针域:存储指向下一个节点的指针

在单向链表中,节点通过指针域相互链接,形成一个线性结构。链表的最后一个节点指向 `NULL`,表示链表的结束。

C 语言中单向链表的定义:

struct Node {
    int data;          // 数据域
    struct Node *next; // 指针域,指向下一个节点
};

1.2 操作与实现

插入节点
插入节点通常有几种方式:
- 在链表头插入节点
- 在链表尾插入节点
- 在链表中的某个位置插入节点

在链表头插入节点的代码示例:

void insert_at_head(struct Node **head, int new_data) {
    struct Node *new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node->data = new_data;
    new_node->next = *head;
    *head = new_node;
}
 

删除节点
删除节点同样可以根据位置(头节点、尾节点、中间节点)来进行操作。

删除指定值节点的代码示例:

void delete_node(struct Node **head, int key) {
    struct Node *temp = *head, *prev;

    if (temp != NULL && temp->data == key) {
        *head = temp->next; // 如果头节点就是要删除的节点
        free(temp);
        return;
    }

    while (temp != NULL && temp->data != key) {
        prev = temp;
        temp = temp->next;
    }

    if (temp == NULL) return; // 如果没有找到该节点

    prev->next = temp->next; // 解除该节点与链表的链接
    free(temp);
}
 

遍历链表
遍历链表是通过一个临时指针从头到尾遍历所有节点。

遍历链表的代码示例:

void print_list(struct Node *node) {
    while (node != NULL) {
        printf("%d -> ", node->data);
        node = node->next;
    }
    printf("NULL\n");
}
 

1.3 适用场景
单向链表适合用于需要频繁在表头插入或删除元素的场景,例如:
- 实现堆栈(栈顶元素可以快速插入和删除)
- 需要动态增加或删除元素且对内存要求敏感时

然而,由于单向链表只能向一个方向遍历,查找和插入某些特定位置的性能较差。

2. 双向链表(Doubly Linked List)

2.1 定义与结构
双向链表相比单向链表,增加了对每个节点前驱节点的引用。每个节点包含三个部分:
- 数据域:存储数据
- 前驱指针域:指向上一个节点
- 后继指针域:指向下一个节点

C 语言中双向链表的定义:

struct Node {
    int data;
    struct Node *prev; // 前驱指针
    struct Node *next; // 后继指针
};
 

2.2 操作与实现
由于双向链表的节点有两个指针,操作和单向链表相比稍显复杂,但在链表的遍历、删除和插入操作中,灵活性更强。

插入节点
在链表头插入节点:

void insert_at_head(struct Node **head, int new_data) {
    struct Node *new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node->data = new_data;
    new_node->next = *head;
    new_node->prev = NULL;

    if (*head != NULL) {
        (*head)->prev = new_node;
    }

    *head = new_node;
}
 

删除节点
删除指定节点:

void delete_node(struct Node **head, struct Node *del_node) {
    if (*head == NULL || del_node == NULL) return;

    if (*head == del_node) *head = del_node->next;

    if (del_node->next != NULL) del_node->next->prev = del_node->prev;

    if (del_node->prev != NULL) del_node->prev->next = del_node->next;

    free(del_node);
}
 

遍历链表
双向链表可以从任意节点向前或向后遍历,提供了更灵活的遍历方式。

从头到尾遍历双向链表的代码:

void print_list(struct Node *node) {
    struct Node *last;
    printf("Traversal in forward direction: ");
    while (node != NULL) {
        printf("%d -> ", node->data);
        last = node;
        node = node->next;
    }
    printf("NULL\n");

    printf("Traversal in reverse direction: ");
    while (last != NULL) {
        printf("%d -> ", last->data);
        last = last->prev;
    }
    printf("NULL\n");
}
 

2.3 适用场景
双向链表特别适用于需要双向遍历的场景,例如:
- 实现双端队列(Deque)
- 实现导航系统,用户可以前后移动(如浏览器的前进/后退操作)

双向链表的缺点是每个节点需要额外的空间来存储 `prev` 指针,相比单向链表占用更多内存。

3. 内核双向链表(Linux Kernel Doubly Linked List)

3.1 定义与结构
在 Linux 内核中,双向链表的实现采用了高度模块化和通用化的设计。Linux 内核中的双向链表不会直接与数据绑定,而是通过一个独立的 `list_head` 结构体表示链表节点的指针。这使得链表可以灵活地嵌入任意数据结构中。

Linux 内核双向链表的定义:

struct list_head {
    struct list_head *next, *prev;
};
 

每个节点的数据存储在包含 `list_head` 的外部数据结构中。

3.2 操作与实现
内核提供了一套高效的宏和函数来简化双向链表的操作。例如:
- *`INIT_LIST_HEAD(&head)`:初始化链表头节点。
- `list_add()` 和 `list_add_tail()`:在链表头或尾部插入节点。
- `list_del()`:删除节点。

插入节点示例

struct my_data {
    int value;
    struct list_head list;
};

struct list_head my_list;
INIT_LIST_HEAD(&my_list);

struct my_data data1 = { .value = 1 };
list_add(&data1.list, &my_list);
 

遍历节点示例
使用宏遍历链表中的每个节点:

struct my_data *entry;
list_for_each_entry(entry, &my_list, list) {
    printk(KERN_INFO "Value: %d\n", entry->value);
}
 

3.3 适用场景
Linux 内核双向链表广泛用于内核中的各种模块,例如:
- 实现任务队列
- 管理内核资源
- 设备驱动程序中的资源管理

内核双向链表特别适合场景复杂、需要高度通用性和高效链表操作的场合。

4. 总结与比较

5. 选择建议
- 单向链表:适合于内存受限、操作简单、只需要单向遍历的场景,如堆栈、队列。
- 双向链表:适合需要双向遍历、插入和删除频繁的场景,如双端队列、双向导航应用。
- 内核双向链表:适用于高效链表操作、复杂系统管理场景,尤其是嵌入式系统、内核模块和驱动开发。

这篇关于linux下c语言中的单向列表,双向链表,内核双向列表,及适用场景的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1140334

相关文章

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Linux samba共享慢的原因及解决方案

《Linuxsamba共享慢的原因及解决方案》:本文主要介绍Linuxsamba共享慢的原因及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux samba共享慢原因及解决问题表现原因解决办法总结Linandroidux samba共享慢原因及解决

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

Linux安装MySQL的教程

《Linux安装MySQL的教程》:本文主要介绍Linux安装MySQL的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux安装mysql1.Mysql官网2.我的存放路径3.解压mysql文件到当前目录4.重命名一下5.创建mysql用户组和用户并修

Linux上设置Ollama服务配置(常用环境变量)

《Linux上设置Ollama服务配置(常用环境变量)》本文主要介绍了Linux上设置Ollama服务配置(常用环境变量),Ollama提供了多种环境变量供配置,如调试模式、模型目录等,下面就来介绍一... 目录在 linux 上设置环境变量配置 OllamPOgxSRJfa手动安装安装特定版本查看日志在

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

Linux系统之dns域名解析全过程

《Linux系统之dns域名解析全过程》:本文主要介绍Linux系统之dns域名解析全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、dns域名解析介绍1、DNS核心概念1.1 区域 zone1.2 记录 record二、DNS服务的配置1、正向解析的配置

C语言实现两个变量值交换的三种方式

《C语言实现两个变量值交换的三种方式》两个变量值的交换是编程中最常见的问题之一,以下将介绍三种变量的交换方式,其中第一种方式是最常用也是最实用的,后两种方式一般只在特殊限制下使用,需要的朋友可以参考下... 目录1.使用临时变量(推荐)2.相加和相减的方式(值较大时可能丢失数据)3.按位异或运算1.使用临时