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

嵌入式数据结构笔记五——循环链表内核链表

文章目录

  • 前言
  • 一、循环链表:
    • 1.链表尾插法:
    • 2.链表的遍历:
    • 3.链表的查找:
    • 4.链表的修改:
    • 5.链表的删除:
    • 6.链表的销毁:
  • 二、内核链表:
    • 1.概念:
    • 2.结构描述:
    • 3.使用内核链表:
  • 重点


前言

单向链表只能从前往后找,双向链表多了一个从后向前找,循环链表可以从最后一个找到第一个。但我们所使用的链表只能存放一种数据类型,因此引入了内核链表的概念。


正文内容:

一、循环链表:

1.链表尾插法:

在这里插入图片描述

  • 申请节点空间
  • 将数据存放到节点中
  • 将pnext赋值为空白节点
  • 将ppre赋值为之前的最后一个节点地址
  • 将之前最后一个节点的pnext指向新申请节点
  • 将空白节点的ppre指向新申请节点

代码如下:

/* 尾插法 */
int insert_tail_linklist(linknode *phead, datatype tmpdata)
{linknode *ptmpnode = NULL;ptmpnode = malloc(sizeof(linknode));if (NULL == ptmpnode){perror("fail to malloc");return -1;}ptmpnode->data = tmpdata;ptmpnode->pnext = phead;ptmpnode->ppre = phead->ppre;phead->ppre->pnext = ptmpnode;phead->ppre = ptmpnode;//上两句同等写法//ptmpnode->pnext->ppre = ptmpnode;//ptmpnode->ppre->pnext = ptmpnode;return 0;
}

2.链表的遍历:

在这里插入图片描述

  • 循环链表最终循环条件由 != NULL修改为 != phead

代码如下:

/* 链表节点遍历 */
int show_linklist(linknode *phead)
{linknode *ptmpnode = NULL;ptmpnode = phead->pnext;while (ptmpnode != phead){printf("%d ", ptmpnode->data);ptmpnode = ptmpnode->pnext;}printf("\n");return 0;
}

3.链表的查找:

参考双向链表查找

代码如下:

/* 链表的查找 */
linknode *find_linklist(linknode *phead, datatype tmpdata)
{linknode *ptmpnode = NULL;ptmpnode = phead->pnext;while (ptmpnode != phead){if (ptmpnode->data == tmpdata){return ptmpnode;}ptmpnode = ptmpnode->pnext;}return NULL;
}

4.链表的修改:

参考双向链表修改

代码如下:

/* 链表的修改 */
int update_linklist(linknode *phead, datatype olddata, datatype newdata)
{linknode *ptmpnode = NULL;ptmpnode = phead->pnext;while (ptmpnode != phead){if (ptmpnode->data == olddata){ptmpnode->data = newdata;}ptmpnode = ptmpnode->pnext;}return 0;
}

5.链表的删除:

参考双向链表的删除

代码如下:

/* 链表的删除 */
int delete_linklist(linknode *phead, datatype tmpdata)
{linknode *ptmpnode = NULL;linknode *pfreenode = NULL;ptmpnode = phead->pnext;while (ptmpnode != phead){if (ptmpnode->data == tmpdata){ptmpnode->ppre->pnext = ptmpnode->pnext;ptmpnode->pnext->ppre = ptmpnode->ppre;pfreenode = ptmpnode;ptmpnode = ptmpnode->pnext;free(pfreenode);}else{ptmpnode = ptmpnode->pnext;}}return 0;
}

6.链表的销毁:

参考双向链表的销毁

代码如下:

/* 链表的销毁 */
int destroy_linklist(linknode **pphead)
{linknode *ptmpnode = NULL;linknode *pfreenode = NULL;ptmpnode = (*pphead)->pnext;pfreenode = ptmpnode;while (ptmpnode != *pphead){ptmpnode = ptmpnode->pnext;free(pfreenode);pfreenode = ptmpnode;}free(*pphead);*pphead = NULL;return 0;
}

二、内核链表:

1.概念:

  • Linux内核中所使用到的一种链表结构
  • 使用同一种结构可以存储不同类型的链表
  • 普通链表:链表节点中包含数据
  • 内核链表:数据中包含链表节点

2.结构描述:

在这里插入图片描述

  • 创建:创建空白节点即可
  • 插入:申请节点,并按照之前学习的循环链表插入即可(插入的链表节点首地址即数据节点首地
    址)
  • 访问节点:通过遍历找到每个节点的首地址,强制类型转换即可获得对应数据空间首地址

3.使用内核链表:

  • 可以参考list.h中关于内核链表的常见操作:
/*Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>This file is part of GlusterFS.This file is licensed to you under your choice of the GNU LesserGeneral Public License, version 3 or any later version (LGPLv3 orlater), or the GNU General Public License, version 2 (GPLv2), in allcases as published by the Free Software Foundation.
*/#ifndef _LLIST_H
#define _LLIST_H/* 内核链表中的节点类型 */
struct list_head {struct list_head *next;struct list_head *prev;
};/* 初始化空白头结点 */
#define INIT_LIST_HEAD(head) do {			\(head)->next = (head)->prev = head;	\} while (0)/* 头插法 */
static inline void
list_add (struct list_head *new, struct list_head *head)
{new->prev = head;new->next = head->next;new->prev->next = new;new->next->prev = new;
}/* 尾插法 */
static inline void
list_add_tail (struct list_head *new, struct list_head *head)
{new->next = head;new->prev = head->prev;new->prev->next = new;new->next->prev = new;
}/* 按指定顺序插入 */
/* This function will insert the element to the list in a order.Order will be based on the compare function provided as a input.If element to be inserted in ascending order compare should return:0: if both the arguments are equal>0: if first argument is greater than second argument<0: if first argument is less than second argument */
static inline void
list_add_order (struct list_head *new, struct list_head *head,int (*compare)(struct list_head *, struct list_head *))
{struct list_head *pos = head->prev;while ( pos != head ) {if (compare(new, pos) >= 0)break;/* Iterate the list in the reverse order. This will havebetter efficiency if the elements are inserted in theascending order */pos = pos->prev;}list_add (new, pos);
}
/* 将节点移出所属的链表 */
static inline void
list_del (struct list_head *old)
{old->prev->next = old->next;old->next->prev = old->prev;old->next = (void *)0xbabebabe;old->prev = (void *)0xcafecafe;
}/* 将节点移出所属的链表,并初始化 */
static inline void
list_del_init (struct list_head *old)
{old->prev->next = old->next;old->next->prev = old->prev;old->next = old;old->prev = old;
}/* 将节点移动到另一个链表的头部 */
static inline void
list_move (struct list_head *list, struct list_head *head)
{list_del (list);list_add (list, head);
}/* 将节点移动到另一个链表的尾部 */
static inline void
list_move_tail (struct list_head *list, struct list_head *head)
{list_del (list);list_add_tail (list, head);
}/* 判断链表是否为空 */
static inline int
list_empty (struct list_head *head)
{return (head->next == head);
}/* 将list链表所有元素拼到head链表的前面 */
static inline void
__list_splice (struct list_head *list, struct list_head *head)
{(list->prev)->next = (head->next);(head->next)->prev = (list->prev);(head)->next = (list->next);(list->next)->prev = (head);
}/* 将list链表所有元素拼到head链表的前面 */
static inline void
list_splice (struct list_head *list, struct list_head *head)
{if (list_empty (list))return;__list_splice (list, head);
}/* 将list链表所有元素拼到head链表的前面,并初始化list头结点 */
/* Splice moves @list to the head of the list at @head. */
static inline void
list_splice_init (struct list_head *list, struct list_head *head)
{if (list_empty (list))return;__list_splice (list, head);INIT_LIST_HEAD (list);
}/* 将list链表所有元素追加到head链表的后面 */
static inline void
__list_append (struct list_head *list, struct list_head *head)
{(head->prev)->next = (list->next);(list->next)->prev = (head->prev);(head->prev) = (list->prev);(list->prev)->next = head;
}/* 将list链表所有元素追加到head链表的后面 */
static inline void
list_append (struct list_head *list, struct list_head *head)
{if (list_empty (list))return;__list_append (list, head);
}/* 将list链表所有元素追加到head链表的后面,并初始化list */
/* Append moves @list to the end of @head */
static inline void
list_append_init (struct list_head *list, struct list_head *head)
{if (list_empty (list))return;__list_append (list, head);INIT_LIST_HEAD (list);
}
/* 判断当前节点是否为最后一个节点 */
static inline int
list_is_last (struct list_head *list, struct list_head *head)
{return (list->next == head);
}
/* 判断链表是否只有一个节点 */
static inline int
list_is_singular(struct list_head *head)
{return !list_empty(head) && (head->next == head->prev);
}
/* 将旧节点用新节点替换 */
/*** list_replace - replace old entry by new one* @old : the element to be replaced* @new : the new element to insert** If @old was empty, it will be overwritten.*/
static inline void list_replace(struct list_head *old,struct list_head *new)
{new->next = old->next;new->next->prev = new;new->prev = old->prev;new->prev->next = new;
}
/* 将旧节点用新节点替换,并初始化旧节点 */
static inline void list_replace_init(struct list_head *old,struct list_head *new)
{list_replace(old, new);INIT_LIST_HEAD(old);
}
/* 内核链表左旋 */
/*** list_rotate_left - rotate the list to the left* @head: the head of the list*/
static inline void list_rotate_left (struct list_head *head)
{struct list_head *first;if (!list_empty (head)) {first = head->next;list_move_tail (first, head);}
}
/* 根据链表节点地址找到数据元素首地址 */
#define list_entry(ptr, type, member)					\((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
/* 找到第一个数据元素地址 */
#define list_first_entry(ptr, type, member)     \list_entry((ptr)->next, type, member)
/* 找到最后一个数据元素地址 */
#define list_last_entry(ptr, type, member)     \list_entry((ptr)->prev, type, member)
/* 找到下一个数据元素地址 */
#define list_next_entry(pos, member) \list_entry((pos)->member.next, typeof(*(pos)), member)
/* 找到上一个数据元素地址 */
#define list_prev_entry(pos, member) \list_entry((pos)->member.prev, typeof(*(pos)), member)
/* 遍历链表节点元素地址 */
#define list_for_each(pos, head)                                        \for (pos = (head)->next; pos != (head); pos = pos->next)
/* 遍历所有数据元素首地址 */
#define list_for_each_entry(pos, head, member)				\for (pos = list_entry((head)->next, typeof(*pos), member);	\&pos->member != (head); 					\pos = list_entry(pos->member.next, typeof(*pos), member))/* 遍历所有数据元素首地址(可以在遍历过程中修改数据元素指针) */
#define list_for_each_entry_safe(pos, n, head, member)			\for (pos = list_entry((head)->next, typeof(*pos), member),	\n = list_entry(pos->member.next, typeof(*pos), member);	\&pos->member != (head); 					\pos = n, n = list_entry(n->member.next, typeof(*n), member))
/* 倒着遍历所有数据元素首地址 */
#define list_for_each_entry_reverse(pos, head, member)                  \for (pos = list_entry((head)->prev, typeof(*pos), member);      \&pos->member != (head);                                    \pos = list_entry(pos->member.prev, typeof(*pos), member))/* 倒着遍历所有数据元素首地址(可以在遍历过程中修改数据元素指针) */
#define list_for_each_entry_safe_reverse(pos, n, head, member)          \for (pos = list_entry((head)->prev, typeof(*pos), member),      \n = list_entry(pos->member.prev, typeof(*pos), member); \&pos->member != (head);                                    \pos = n, n = list_entry(n->member.prev, typeof(*n), member))/** This list implementation has some advantages, but one disadvantage: you* can't use NULL to check whether you're at the head or tail.  Thus, the* address of the head has to be an argument for these macros.*/
/* 获得下一个数据元素空间首地址,如果没有返回NULL */
#define list_next(ptr, head, type, member)      \(((ptr)->member.next == head) ? NULL    \: list_entry((ptr)->member.next, type, member))
/* 获得上一个数据元素空间首地址,如果没有返回NULL */
#define list_prev(ptr, head, type, member)      \(((ptr)->member.prev == head) ? NULL    \: list_entry((ptr)->member.prev, type, member))#endif /* _LLIST_H */
  • 使用内核链表插入元素1,2,3并遍历:
#include "list.h"
#include <stdio.h>/* 数据类型 */
typedef struct data
{struct list_head node; // 节点int value;             // 数据元素
} data_t;int main(void)
{struct list_head head;data_t num1 = {.value = 1,};data_t num2 = {.value = 2,};data_t num3 = {.value = 3,};struct list_head *ptmpnode = NULL;INIT_LIST_HEAD(&head);#if 0list_add_tail(&num1.node, &head);list_add_tail(&num2.node, &head);list_add_tail(&num3.node, &head);
#endiflist_add(&num1.node, &head);list_add(&num2.node, &head);list_add(&num3.node, &head);list_for_each(ptmpnode, &head)//宏定义展开{
//        printf("%d ", ((data_t *)ptmpnode)->value);printf("%d ", list_entry(ptmpnode, data_t, node)->value);}printf("\n");return 0;
}

重点

  • 循环链表在操作时与双向链表的区别
  • 内核链表的使用

文章转载自:

http://AgZ019Go.Lqjpb.cn
http://BhX8qkAI.Lqjpb.cn
http://UGg6Bnoh.Lqjpb.cn
http://PnI4rSt4.Lqjpb.cn
http://8En6Qezz.Lqjpb.cn
http://Od7RGg2m.Lqjpb.cn
http://nnYL9ZAF.Lqjpb.cn
http://by8zbOvQ.Lqjpb.cn
http://DStK33SZ.Lqjpb.cn
http://0Hbm7qJU.Lqjpb.cn
http://kZWN84Wh.Lqjpb.cn
http://8uViwi7X.Lqjpb.cn
http://TENRDNO5.Lqjpb.cn
http://sfepiUgB.Lqjpb.cn
http://i84vtS0v.Lqjpb.cn
http://3928UU48.Lqjpb.cn
http://1azPAtXa.Lqjpb.cn
http://z0qy2EVO.Lqjpb.cn
http://SxBBt2MK.Lqjpb.cn
http://l7OR7THx.Lqjpb.cn
http://GSzuljJ7.Lqjpb.cn
http://eR6fgU49.Lqjpb.cn
http://fae16qTC.Lqjpb.cn
http://g3kO3bWv.Lqjpb.cn
http://2Z4TQMVy.Lqjpb.cn
http://MnlxhZ2D.Lqjpb.cn
http://BHisxsCU.Lqjpb.cn
http://9HK8ac8d.Lqjpb.cn
http://d6Nj5JJd.Lqjpb.cn
http://niGevbVj.Lqjpb.cn
http://www.dtcms.com/a/385806.html

相关文章:

  • C++与Lua交互:从原理到实践指南
  • 状态管理:在 Next.js 中使用 React Context 或 Zustand
  • SeaweedFS深度解析(九):k8s环境使用helm部署Seaweedfs集群
  • uniApp开发XR-Frame微信小程序创建3D场景 (8) 刚体碰撞
  • NPM 常用命令
  • Windows 11 安装使用 nvm,Node.js、npm多版本管理、切换
  • AI Compass前沿速览:GPT-5-Codex 、宇树科技世界模型、InfiniteTalk美团数字人、ROMA多智能体框架、混元3D 3.0
  • 苹果上架全流程指南 苹果应用上架步骤、iOS 应用发布流程、uni-app 打包上传 ipa 与 App Store 审核经验分享
  • 旗讯 OCR 识别系统深度解析:一站式解决表格、手写文字、证件识别难题!
  • strip()函数使用注意点
  • 好用的开源日志库:Easylogger解析与移植STM32
  • django入门-数据库基本操作
  • springboot的项目实现excel上传功能
  • 从 Docker 守护进程获取实时事件
  • TCP编程:socket概念及使用方法(基础教程)
  • Python 在运维与云原生领域的核心应用:从基础到实践
  • 项目实战:Rsync + Sersync 实现文件实时同步
  • 云原生是什么
  • Docker 镜像瘦身实战:从 1.2GB 压缩到 200MB 的优化过程
  • RabbitMQ消息中间件
  • 2019年下半年 系统架构设计师 案例分析
  • OpenAI编程模型重磅升级!GPT-5-Codex发布,动态思考机制实现编程效率倍增
  • 数据结构排序入门(2):核心排序(选择排序,快速排序及优化)
  • 达索系统 SIMULIA 大中华区用户大会启幕,迅筑科技分享设计仿真一体化落地方案
  • 未来已来:当清洁成为一场静默的科技交响
  • 从零开始手写机器学习框架:我的深度学习之旅
  • Qt QML Switch和SwitchDelegate的区别?
  • MATLAB 线弹性 + 裂纹扩展 1D2D3D 统一框架
  • 基于Qt的跨平台全局输入事件监控技术实现
  • 从0到1入门JVM