当前位置: 首页 > news >正文

数据结构(四)内核链表、栈与队列

一、内核链表基础

1. 什么是 Linux 内核链表?

Linux 内核链表是一种高效的 双向循环链表,广泛应用于内核模块开发中,用于管理数据结构。每个节点通过指针连接前一个和后一个元素,实现插入和删除的高性能。

2. 链表的定义与初始化

在 Linux 内核中,链表的实现依赖于 struct list_head 结构体,定义在头文件 <linux/list.h> 中:

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

为了在自定义结构体中集成链表,只需在结构体中包含 list_head 成员即可:

struct my_data {
int value;
struct list_head list;
}

链表初始化使用如下宏:

LIST_HEAD(my_list); // 静态初始化
// 或者
INIT_LIST_HEAD(&my_list); // 动态初始化

这两种方式都将 list.nextlist.prev 指向自身,构成一个空的循环链表。

注意:内核链表已经将增删查改封装好了,开发者只需要聚焦于数据部分。


3. 内核链表设计思想

  • 普通链表将数据和节点结构紧耦合。

  • 内核链表则通过将链表节点作为结构体中的一个字段,从而实现结构体与链表解耦。

常用宏如下:

  • offsetof():用于计算某个成员在结构体中的偏移量。

  • container_of():用于通过结构体成员地址获取整个结构体指针。

#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))


4. 常用链表操作宏与函数

功能宏 / 函数名称说明
添加节点list_add / list_add_tail添加到链表头/尾
删除节点list_del将节点从链表中移除
遍历链表list_for_each遍历每一个节点(通用)
遍历链表list_for_each_entry遍历包含结构体的链表

5. 使用链表需注意的要点

  • 内存管理:新增节点前需手动分配内存;删除节点后应释放。

  • 线程安全:多线程环境下需加锁。

  • 性能考量:链表适合插入/删除频繁场景,但查找效率低。


二、栈(Stack)

1. 基本定义

栈是一种 只允许在一端(栈顶)进行插入和删除操作 的线性结构,遵循 后进先出(LIFO) 原则。

在 C 语言中,我们通常用动态内存(堆区)来实现线性栈结构。

2. 结构特点

  • 栈顶(top):允许插入/删除的一端。

  • 栈底(bottom):固定不动的一端。

  • 空栈:栈中无任何数据元素。

3. 栈的类型

  • 满增栈:栈满时从低地址向高地址增长。

  • 空减栈:从高地址向低地址扩展。

  • 实际实现时通常选择一种逻辑来处理栈顶移动。

📌 示例说明

栈空间从底部开始增长,压栈操作使 top++,出栈使 top--。如果 top 达到容量限制,表示栈已满。

4. 栈的应用场景

  • 递归求解 / 回溯问题

  • 表达式求值(符号优先级)

  • 函数调用管理(程序栈帧)


5. 栈的基本操作代码

#include <stdio.h>
#include "stack.h"
#include <stdlib.h>Stack *creatStack()
{Stack *pstack = malloc(sizeof(Stack));if(pstack == NULL){printf("malloc error\n");return NULL;}pstack->ptop = NULL;pstack->clen = 0;return pstack;
}int IsEmptyStack(Stack *pstack)
{return NULL == pstack->ptop;
}
int pushStack(Stack *pstack, DataType data)
{StNode *pnode = malloc(sizeof(StNode));if(NULL == pnode){printf("malloc error\n");return -1;}pnode->data = data;pnode->pnext = NULL;pnode->pnext = pstack->ptop;pstack->ptop = pnode;pstack->clen++;return 1;
}/*** @brief 出栈,且出栈之前保存栈顶的元素,通过主函数定义的dadatype类型返回改后的数值* * @param pstack 栈头指针* @param data 返回的栈顶元素的值* @return int 成功出栈返回1,失败为0*/
int popStack(Stack *pstack, DataType *data)
{if(IsEmptyStack(pstack)){return -1;}StNode *temp = pstack->ptop;*data = temp->data;pstack->ptop = temp->pnext;free(temp);pstack->clen--;return 1;
}/*** @brief Get the Stack Top object* * @param pstack * @param data * @return int */
int getStackTop(Stack *pstack, DataType *data)
{if(IsEmptyStack(pstack)){return -1;}*data = pstack->ptop->data;return 1;
}void printStack(Stack *pstack)
{if(IsEmptyStack(pstack)){return ;}StNode *temp = pstack->ptop;while(temp){printf("%d ", temp->data);temp = temp->pnext;}puts("");return ;
}void destroyStack(Stack **pstack)
{StNode *temp = (*pstack)->ptop;while(temp){(*pstack)->ptop = temp->pnext;free(temp);temp = (*pstack)->ptop;}free(*pstack);*pstack = NULL;
}

三、队列(Queue)

1. 基本定义

队列是一种**先进先出(FIFO)**的数据结构,特点是:

  • 队尾(tail) 进入数据

  • 队头(head) 移除数据

2. 队列的应用场景

  • 缓冲区管理

  • 数据包调度

  • 多任务之间的通信机制


3. 循环队列(环形队列)

为避免频繁移动数据,可将队列逻辑设计为循环结构。即:利用数组,头尾指针移动时利用求余操作形成环形效果。

next = (tail + 1) % MAX_LEN;

判断条件:
队列状态条件
队空head == tail
队满(tail + 1) % MAX == head

4. 队列的类型

  • 顺序队列(数组实现)

  • 链式队列(链表实现,扩展性强)


5. 链表型队列的代码

#include <stdio.h>
#include "linkqueue.h"
#include <stdlib.h>LQueue *creatQLink()
{LQueue *plink = malloc(sizeof(LQueue));if(NULL == plink){fprintf(stderr, "malloc error\n");return NULL;}plink->phead = NULL;plink->ptail = NULL;plink->clen = 0;return plink;
}int isEmptyLQueue(LQueue *plink)
{return NULL == plink->phead;
}int pushLQueue(LQueue *plink, DataType data)
{LQNode *pnode = malloc(sizeof(LQNode));if(pnode == NULL){fprintf(stderr, "malloc error\n");return -1;}pnode->data = data;pnode->pnext = NULL;if(isEmptyLQueue(plink)){plink->phead = pnode;plink->ptail = pnode;plink->clen++;}else{plink->ptail->pnext = pnode;plink->ptail = pnode;plink->clen++;}return 0;
}int popLQueue(LQueue *plink)
{if(isEmptyLQueue(plink)){fprintf(stderr,"is empty lqueue\n");return -1;}LQNode *temp = plink->phead;plink->phead = temp->pnext;if(NULL == temp->pnext){plink->ptail = NULL;}free(temp);plink->clen--;return 0;
}LQNode* getLQueueHeadNode(LQueue *plink)
{if(isEmptyLQueue(plink)){fprintf(stderr, "empty Lqueue\n");return NULL;}return plink->phead;
}void printLQueue(LQueue *plink)
{if(isEmptyLQueue(plink)){fprintf(stderr, "empty lqueue\n");return ;}LQNode *temp = plink->phead;while(temp){printf("%d ", temp->data);temp = temp->pnext;}puts("");return ;
}void destroyLQueue(LQueue **plink)
{LQNode *temp = (*plink)->phead;while(temp){(*plink)->phead = temp->pnext;free(temp);temp = (*plink)->phead;}free(*plink);*plink = NULL;return ;
}

http://www.lryc.cn/news/611660.html

相关文章:

  • JAVA无人系统台球茶室棋牌室系统支持H5小程序APP公众号源码
  • Python Pandas.lreshape函数解析与实战教程
  • 基于Simulink/MWORKS的文字与开关量混合传输系统设计
  • Godot ------ 初级人物血条制作02
  • 符合网络安全的汽车OTA软件更新分发机制
  • DHCP 服务器练习
  • Nacos配置中心和数据隔离在idea中的实现
  • R4周打卡——Pytorch实现 LSTM火灾预测
  • Ansys Discovery 2025R2的主要增强功能:CFD仿真的亮点
  • 批量打印Excel条形码
  • 西门子PLC基础指令6:读取时钟指令、设置时钟指令、使能含义与注意
  • 《动手学深度学习》读书笔记—9.5机器翻译与数据集
  • miniExcel一个对象加一个对象列表导出
  • 前端全栈修炼手册:从 Vue3 到工程化的进阶之路
  • 线上Linux服务器的优化设置、系统安全与网络安全策略
  • 移动商城平台适配:ZKmall开源商城鸿蒙 / 小程序端开发要点
  • django permission_classes = [AllowAny] 如何限制到具体接口
  • 时间轮算法
  • Java学习第一百一十一部分——Jenkins(二)
  • docker-compose快速部署启动file beat+ELK
  • Git 分支管理:从新开发分支迁移为主分支的完整指南
  • Agent安全机制:权限控制与风险防范
  • 商派小程序商城(小程序/官网/APP···)的范式跃迁与增长再想象
  • C语言基础_排序算法和二分法查找
  • GROUP BY与ORDER BY的索引优化方法
  • 脑洞大开——AI流程图如何改变思维?
  • 深入解析Java NIO在高并发场景下的性能优化实践指南
  • 企业网络安全中人工智能(AI)的影响
  • 使用MatterJs物理2D引擎实现重力和鼠标交互等功能,有点击事件(盒子堆叠效果)
  • HTML应用指南:利用GET请求获取全国OPPO官方授权体验店门店位置信息