之前看到一篇单向链表的博文,代码也看着很舒服,于是乎记录下来,留给自己~,循序渐进,慢慢

延伸到真正的内核链表~(敢问路在何方?路在脚下~)

1. 简介

链表是Linux 内核中最简单,最普通的数据结构。链表是一种存放和操作可变数量元素(常称为节点)

的数据结构,链表和静态数组的不同之处在于,它所包含的元素都是动态创建并插入链表的,在编译

时不必知道具体需要创建多少个元素,另外也因为链表中每个元素的创建时间各不相同,所以它们在

内存中无须占用连续内存区。正是因为元素不连续的存放,所以各个元素需要通过某种方式被链接在

一起,于是每个元素都包含一个指向下一个元素的指针,当有元素加入链表或从链表中删除元素时,

简单调整一下节点的指针就可以了。

根据它的特性,链表可分为:单链表双链表单向循环链表和双向循环链表,今天总结记录的就是

最简单的单链表,

1.1 节点类型描述

  1. typedef struct node_t {
  2. data_t data; /* 节点数据域 */
  3. struct node_t *next; /* 节点的后继指针域 */
  4. }linknode_t, *linklist_t;

另一种写法

  1. struct node_t {
  2. data_t data;
  3. struct node_t *next;
  4. }
  5. typedef struct node_t linknode_t;
  6. typedef struct node_t* linklist_t;

细看说明:

* linknode_t A;
* linklist_t p = &A;
*
* 结构变量A为所描述的节点,而指针变量p为指向此类型节点的指针(p值为节点的地址)
* 这样看来 linknode_t 和 linklist_t 的作用是一样的,那么为什么我们要定义两个数据类
* 型(同一种)呢?  答曰:主要为了代码的可读性,我们要求标识符要望文识义,便于理解
*
* linknode_t *pnode  指向一个节点
* linklist_t list  指向一个整体

1.2 头节点 head (~黄河之水天上来~)

在顺序存储线性表,如何表达一个空表{ },是通过list->last = -1来表现的,所谓的空表就是

数据域为NULL,而链表有数据域和指针域,我们如何表现空链表呢?这时,就引入了头结点

的概念,头结点和其他节点数据类型一样,只是数据域为NULL,head->next = NULL,下面

我们看一个创建空链表的函数,如何利用头结点来创建一个空链表,只要头节点在,链表就还在~

  1. // 创建一个空链表
  2. linklist_t
  3. CreateEmptyLinklist()
  4. {
  5. linklist_t list;
  6. list = (linklist_t)malloc(sizeof(linknode_t));
  7.  
  8. if(NULL != list) {
  9. list->next = NULL;
  10. }
  11.  
  12. return list;
  13. }

2. 链表基本运算的相关"算法"操作 or 操刀(~烹羊宰牛且为乐,会须一饮三百杯~)

链表的运算除了上面的创建空链表,还有数据的插入,删除,查找等函数,链表的运算有各种实现方

法,如何写出一个高效的,封装性较好的函数是我们要考虑的,比如数据插入函数,我们就要尽可能

考虑所有能出现的结果,比如:1)如果需插入数据的链表是个空表;2)所插入的位置超过了链表的

长度;如果我们的函数能包含所有能出现的情况,不仅能大大提高我们的开发效率,也会减少代码的

错误率。下面看一个可用性较强的链表插入操作的函数实现~

  1. int
  2. InsertLinklist(linklist_t list, int at, data_t x)
  3. {
  4. linknode_t * node_prev, * node_at, * node_new;
  5. int pos_at;
  6. int found = ;
  7.  
  8. if (NULL == list) return -;
  9.  
  10. /* at must >= 0 */
  11. if (at < ) return -;
  12.  
  13. /*第一步、新节点分配空间*/
  14. node_new = (linklist_t)malloc(sizeof(linknode_t));
  15. if (NULL == node_new) {
  16. return -;
  17. }
  18. node_new->data = x; /* assigned value */
  19. /*
  20. *节点如果插入超过链表长度的位置,会接到尾节点后面,
  21. *这样,node_new成了尾节点,node_new->next = NULL
  22. */
  23. node_new->next = NULL;
  24.  
  25. /*第二步、定位*/
  26. node_prev = list; //跟随指针,帮助我们更好的定位
  27. node_at = list->next; //遍历指针
  28. pos_at = ;
  29. while(NULL != node_at) {
  30. if(pos_at == at){
  31. found = ; //找到正确的位置,跳出循环
  32. break;
  33. }
  34. /* move to the next pos_at */
  35. node_prev = node_at; //跟随指针先跳到遍历指针的位置
  36. node_at = node_at->next; //遍历指针跳到下一个节点的位置
  37. pos_at++;
  38. }
  39.  
  40. /*第三步、插入*/
  41. if(found) {
  42. /* found = 1,找到正确的位置,插入 */
  43. node_new->next = node_at; //插入的节点next指向node_at
  44. node_prev->next = node_new; //插入节点的前一个节点
  45. }
  46. else {
  47. /*若是没找到正确的位置,即所插入位置超越了链表的长度,
  48. *则接到尾节点的后面,同样,这样适用于{ }即空链表,这样
  49. *我们可以建立一个空链表,利用这个函数,实现链表的初始化
  50. */
  51. node_prev->next = node_new;
  52. }
  53.  
  54. return ;
  55. }

3. 正文开始 Demo(~与君歌一曲,请君为我倾耳听~)

listlink.h

  1. #ifndef _LIST_LINK_H_
  2. #define _LIST_LINK_H_
  3.  
  4. typedef int data_t;
  5.  
  6. typedef struct node_t {
  7. data_t data; /* 节点数据域 */
  8. struct node_t * next; /* 节点的后继指针域 */
  9. }linknode_t, * linklist_t;
  10.  
  11. /* 链表操作函数*/
  12.  
  13. // 创建一个空链表
  14. linklist_t CreateEmptyLinklist();
  15.  
  16. // 销毁链表
  17. void DestroyLinklist(linklist_t list);
  18.  
  19. // 清空链表
  20. void ClearLinklist(linklist_t list);
  21.  
  22. // 是否为空链表
  23. int IsEmptyLinklist(linklist_t list);
  24.  
  25. // 链表长度
  26. int LengthLinklist(linklist_t list);
  27.  
  28. // 获去链表节点数据
  29. int GetLinklist(linklist_t list, int at, data_t * x);
  30.  
  31. // 设置链表节点数据
  32. int SetLinklist(linklist_t list, int at, data_t x);
  33.  
  34. // 插入节点
  35. int InsertLinklist(linklist_t list, int at, data_t x);
  36.  
  37. // 删除节点
  38. int DeleteLinklist(linklist_t list, int at);
  39.  
  40. // 链表转置
  41. linklist_t ReverseLinklist(linklist_t list);
  42.  
  43. // 打印链表
  44. int Display(linklist_t list);
  45.  
  46. #endif // _LIST_LINK_H_

listlink.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "listlink.h"
  4.  
  5. // 创建一个空链表
  6. linklist_t
  7. CreateEmptyLinklist()
  8. {
  9. linklist_t list;
  10. list = (linklist_t)malloc(sizeof(linknode_t));
  11.  
  12. if(NULL != list) {
  13. list->next = NULL;
  14. }
  15.  
  16. return list;
  17. }
  18.  
  19. // 销毁链表
  20. void
  21. DestroyLinklist(linklist_t list)
  22. {
  23. if(NULL != list) {
  24. ClearLinklist(list);
  25. free(list);
  26. }
  27. }
  28.  
  29. // 清空链表
  30. void
  31. ClearLinklist(linklist_t list)
  32. {
  33. linknode_t * node; /* pointer to the node to be remove */
  34. if(NULL == list) return;
  35.  
  36. while(NULL != list->next) {
  37. node = list->next;
  38. list->next = node->next; //此时node->next是第二node节点元素依次往后
  39. free(node);
  40. }
  41. return;
  42. }
  43.  
  44. // 是否为空链表
  45. int
  46. IsEmptyLinklist(linklist_t list)
  47. {
  48. if(NULL != list) {
  49. if(NULL == list->next) // 只有头节点
  50. return ; // 返回1,是个空链表
  51. else
  52. return ; // 返回0,链表非空
  53.  
  54. } else
  55. return -; // 返回-1, 错误的类型
  56. }
  57.  
  58. // 链表长度
  59. int
  60. LengthLinklist(linklist_t list)
  61. {
  62. int len = ;
  63. linknode_t * node; // 遍历指针
  64.  
  65. if(NULL == list) return -;
  66.  
  67. node = list->next; // node指针指向第一个节点
  68. while(NULL != node) {
  69. len++;
  70. node = node->next;
  71. }
  72.  
  73. return len;
  74. }
  75.  
  76. // 获去一个链表指定节点数据域的数据值
  77. int
  78. GetLinklist(linklist_t list, int at, data_t * x)
  79. {
  80. linknode_t *node; // 遍历节点的指针
  81. int pos; // 用于遍历比较
  82.  
  83. if(NULL == list) return -;
  84. /*at 必须要 >= 0*/
  85. if(at < ) return -;
  86.  
  87. /* 从第一个元素开始 */
  88. node = list->next; // node指针指向一个元素
  89. pos = ;
  90. while(NULL != node) {
  91. if(at == pos) {
  92. if(NULL != x)
  93. *x = node->data;
  94. return ;
  95. }
  96. // 下一个元素
  97. node = node->next;
  98. pos++;
  99. }
  100. return -;
  101. }
  102.  
  103. // 设置一个指定链表节点的数据域值
  104. int
  105. SetLinklist(linklist_t list, int at, data_t x)
  106. {
  107. linknode_t * node; // 遍历链表
  108. int pos;
  109. int found = ;
  110.  
  111. if(!list) return -;
  112. /*at 必须 >= 0*/
  113. if(at < ) return -;
  114.  
  115. /* node指针指向第一个元素 */
  116. node = list->next;
  117. pos = ;
  118. while(NULL != node) {
  119. if(at == pos) {
  120. found = ; // 找到了位置
  121. node->data = x;
  122. break;
  123. }
  124. /*往后移动元素*/
  125. node = node->next;
  126. pos++;
  127. }
  128. if( == found)
  129. return ;
  130. else
  131. return -;
  132. }
  133.  
  134. // 插入节点
  135. int
  136. InsertLinklist(linklist_t list, int at, data_t x)
  137. {
  138. /*
  139. * node_at and pos_at are used to locate the position of node_at.
  140. * node_prev follows the node_at and always points to previous node
  141. * of node_at.
  142. * node_new is used to point to the new node to be inserted.
  143. */
  144. linknode_t * node_prev, * node_at, * node_new;
  145. int pos_at;
  146. int found = ;
  147.  
  148. if(NULL == list) return -;
  149.  
  150. /* at 必须 >= 0 */
  151. if(at < ) return -;
  152.  
  153. node_new = malloc(sizeof(linknode_t));
  154. if(NULL == node_new)
  155. return -;
  156. node_new->data = x; // assigned value
  157. node_new->next = NULL;
  158.  
  159. node_prev = list; // head
  160. node_at = list->next; //node_at指针指向第一元素
  161. pos_at = ;
  162. while(NULL != node_at) {
  163. if(pos_at == at) {
  164. found = ; // found the node ‘at'
  165. break;
  166. }
  167. /* move to the next pos_at */
  168. node_prev = node_at;
  169. node_at = node_at->next;
  170. pos_at++;
  171. }
  172.  
  173. if(found) {
  174. /* insert */
  175. node_new->next = node_at;
  176. node_prev->next = node_new;
  177. } else{
  178. /*
  179. * If not found,means the provided 'at'
  180. * exceeds the upper limit of the list, just
  181. * append the new node to the end of the list
  182. */
  183. node_prev->next = node_new;
  184. }
  185.  
  186. return ;
  187. }
  188.  
  189. // 删除节点
  190. int
  191. DeleteLinklist(linklist_t list, int at)
  192. {
  193. /*
  194. * node_at and pos_at are used to locate the position of node_at.
  195. * node_prev follows the node_at and always points to previous node
  196. * of node_at.
  197. */
  198. linknode_t * node_prev, * node_at;
  199. int pos_at;
  200. int found = ;
  201.  
  202. if(!list) return -;
  203. if(at < ) return -;
  204.  
  205. node_prev = list; // node_prev指针指向链表头
  206. node_at = list->next; // node_at指针指向第一元素
  207. pos_at = ;
  208.  
  209. while(NULL != node_at) {
  210. if(pos_at == at) {
  211. // found the node 'at'
  212. found = ;
  213. break;
  214. }
  215. // move to the next pos_at
  216. node_prev = node_at;
  217. node_at = node_at->next;
  218. pos_at++;
  219. }
  220. if(found) {
  221. // remove
  222. node_prev->next = node_at->next;
  223. free(node_at);
  224. return ;
  225. }else
  226. return -;
  227. }
  228.  
  229. // 链表转置
  230. linklist_t
  231. ReverseLinklist(linklist_t list)
  232. {
  233. linknode_t * node; // iterator
  234. linknode_t * node_prev; // previous node of iterator
  235. linknode_t * node_next; /* next node of iterator
  236. * used to backup next of iterator
  237. */
  238. if(NULL == list) return NULL;
  239. node_prev = NULL;
  240. node = list->next; // node指针指向第一个元素
  241. while(NULL != node) {
  242. /*
  243. * step1: backup node->next
  244. * due to the next of iterator will be
  245. * modified in step2
  246. */
  247. node_next = node->next;
  248. /*
  249. * when iterator reaches the last node
  250. * of original list, make the list head
  251. * point to the last node, so the original
  252. * last one becomes the first one.
  253. */
  254. if(NULL == node_next)
  255. list->next = node;
  256. /*
  257. * step2: reverse the linkage between nodes
  258. * make the node pointer to the previous node,
  259. * not the next node
  260. */
  261. node->next = node_prev;
  262. /*
  263. * step3: move forward
  264. */
  265. node_prev = node;
  266. node = node_next;
  267. }
  268.  
  269. return list;
  270. }
  271.  
  272. // 打印链表
  273. int
  274. Display(linklist_t list)
  275. {
  276. linknode_t * node;
  277.  
  278. if(NULL == list) return -;
  279.  
  280. node = list->next;
  281. while(node != NULL) {
  282. printf(" %d ", node->data);
  283. node = node->next;
  284. }
  285. printf("\n");
  286.  
  287. return ;
  288. }
  289.  
  290. int main(int argc, char * argv[])
  291. {
  292. int i;
  293. data_t x;
  294. linklist_t p;
  295.  
  296. /*创建链表*/
  297. p = CreateEmptyLinklist();
  298. Display(p);
  299. data_t a[] = {,,,,,,,,,};
  300.  
  301. for(i = ; i < ; i++) {
  302. /*插入链表*/
  303. InsertLinklist(p, i, a[i]);
  304. }
  305. Display(p);
  306.  
  307. /*链表转置*/
  308. ReverseLinklist(p);
  309. /*链表长度*/
  310. printf("The length of the list is [%d]\n", LengthLinklist(p));
  311. Display(p);
  312.  
  313. /*获取特定节点值*/
  314. GetLinklist(p, , &x);
  315. printf("The No.4 of this list is [%d]\n", x);
  316.  
  317. /*设置特定节点的值*/
  318. SetLinklist(p, , );
  319. GetLinklist(p, , &x);
  320. printf("After updating! The No.4 of this list is [%d]\n", x);
  321. Display(p);
  322.  
  323. /*删除节点*/
  324. DeleteLinklist(p,);
  325. printf("After delete!The length of list is [%d]\n", LengthLinklist(p));
  326. Display(p);
  327.  
  328. /*清空链表*/
  329. ClearLinklist(p);
  330. if(IsEmptyLinklist(p))
  331. printf("This list is empty!\n");
  332. /*销毁链表*/
  333. DestroyLinklist(p);
  334. printf("This list is destroyed!\n");
  335.  
  336. return ;
  337.  
  338. }

运行

4. 鸣谢(你动了谁的奶酪? ^_^)

  1. 感谢下面博主的共享,本文的基石,谢谢!!
  2.  
  3. 感谢:https://blog.csdn.net/zqixiao_09/article/details/50402523

5. 后记

行歌

在草长莺飞的季节里喃喃低唱 

到处人潮汹涌还会孤独

怎么

在灯火阑珊处竟然会觉得荒芜

从前轻狂绕过时光        

Linux C 数据结构 ->单向链表<-(~千金散尽还复来~)的更多相关文章

  1. Linux C 数据结构 ->单向链表

    之前看到一篇单向链表的博文,代码也看着很舒服,于是乎记录下来,留给自己~,循序渐进,慢慢 延伸到真正的内核链表~(敢问路在何方?路在脚下~) 1. 简介 链表是Linux 内核中最简单,最普通的数据结 ...

  2. linux内核数据结构之链表

    linux内核数据结构之链表 1.前言 最近写代码需用到链表结构,正好公共库有关于链表的.第一眼看时,觉得有点新鲜,和我之前见到的链表结构不一样,只有前驱和后继指针,而没有数据域.后来看代码注释发现该 ...

  3. linux内核数据结构之链表【转】

    转自:http://www.cnblogs.com/Anker/p/3475643.html 1.前言 最近写代码需用到链表结构,正好公共库有关于链表的.第一眼看时,觉得有点新鲜,和我之前见到的链表结 ...

  4. 数据结构-单向链表 C和C++的实现

    数据结构,一堆数据的存放方式. 今天我们学习数据结构中的 链表: 链表的结构: 链表是一种特殊的数组,它的每个元素称为节点,每个节点包括两个部分: 数据域:存放数据,此部分与数组相同 指针域:存放了下 ...

  5. python数据结构——单向链表

    链表 ( Linked List ) 定义:由许多相同数据类型的数据项按照特定顺序排列而成的线性表. 特点:各个数据在计算机中是随机存放且不连续. 优点:数据的增删改查都很方便,当有新的数据加入的时候 ...

  6. Python3玩转单链表——逆转单向链表pythonic版

    [本文出自天外归云的博客园] 链表是由节点构成的,一个指针代表一个方向,如果一个构成链表的节点都只包含一个指针,那么这个链表就是单向链表. 单向链表中的节点不光有代表方向的指针变量,也有值变量.所以我 ...

  7. 玩转C线性表和单向链表之Linux双向链表优化

    前言: 这次介绍基本数据结构的线性表和链表,并用C语言进行编写:建议最开始学数据结构时,用C语言:像栈和队列都可以用这两种数据结构来实现. 一.线性表基本介绍 1 概念: 线性表也就是关系户中最简单的 ...

  8. C# 单向链表数据结构 (一)

    单向链表数据结构是有节点组成,每个节点包含两部分,第一部分为存储数据,第二部分为指向下一个节点的指针.注意,有两个特色的节点,分别为“头节点”和“尾节点”,头节点本身没有数据,只存储下一个节点的指针, ...

  9. 数据结构(1) 第一天 算法时间复杂度、线性表介绍、动态数组搭建(仿Vector)、单向链表搭建、企业链表思路

    01 数据结构基本概念_大O表示法 无论n是多少都执行三个具体步骤 执行了12步 O(12)=>O(1) O(n) log 2 N = log c N / log c N (相当于两个对数进行了 ...

随机推荐

  1. Flask系列(四)Flask实现简单页面登陆

    from flask import Flask,render_template,request,redirect,session app = Flask(__name__,template_folde ...

  2. 前端页面汉子显示为问号,需修改 linux下面修改mysql 数据库的字符编码为utf8

    设置MySQL数据库编码为UTF-8 登陆后查看数据库当前编码:SHOW VARIABLES LIKE 'char%'; 修改/etc/mysql/my.cnf (默认安装路径下) (标签下没有的添加 ...

  3. PAT 1132 Cut Integer[简单]

    1132 Cut Integer(20 分) Cutting an integer means to cut a K digits lone integer Z into two integers o ...

  4. MessageBox.show显示窗口在最上层

    C#中使用MessageBox.Show();有时候会被主窗口挡住而看不到.使用如下语句则可以使其显示在最上层. MessageBox.Show("MessageBox显示窗口在最上层了吗? ...

  5. Jmeter之Constant Timer与constant throughput timer的区别

    当放置Constant Timer于两个http请求之间,那么它代表的含义是:在上一个请求发出至完成后, 开始Contant Timer指定的时间,最后再发出第二个请求.它并不是代表两个请求之间的发送 ...

  6. bootstrap fileinput api翻译笔记

    bootstrap fileinput HTML标签属性 data-show-preview="false" // 是否显示预览区 multiple // 多文件 data-sho ...

  7. CSS控制列表样式属性list-style有哪些?怎么用?

    CSS列表样式属性list-style有哪些类型?不同类型CSS控制列表样式使用时该注意什么? 这是W3Cschool用户Shirley于2016-11-10在W3Cschool编程问答提出的问题.云 ...

  8. jQuery获取属性

    jQuery在获取jQuery对象的属性时,出现attr()获取不到的情况,此时,请使用prop()获取 如下为经常用到的: var oHtml=$(this).prop("outerHTM ...

  9. OpenResty api 网关

    1,Orange网关 Orange是一个基于OpenResty的API网关.除Nginx的基本功能外,它还可用于API监控.访问控制(鉴权.WAF).流量筛选.访问限速.AB测试.动态分流等.它有以下 ...

  10. (转载)找圆算法((HoughCircles)总结与优化

      Opencv内部提供了一个基于Hough变换理论的找圆算法,HoughCircle与一般的拟合圆算法比起来,各有优势:优势:HoughCircle对噪声点不怎么敏感,并且可以在同一个图中找出多个圆 ...