上一篇我们讲到了线性表,线性表就是数据元素都一一对应,除只有唯一的前驱,唯一的后继。

线性表存储结构分为顺序存储、链式存储。

       顺序存储的优点:

顺序存储的缺点:

       链表就是典型的链式存储,将线性表L = (a0,a1,a2,........an-1)中个元素分布在存储器的不同存储块,成为结点(Node),通过地址或指针建立他们之间的练习,所得到的存储结构为链表结构。表中元素ai的结点形式如下:

其中,结点的data域存放数据元素ai,而next域是一个指针,指向ai的直接后继a(i+1)所在的结点。于是,线性表L=(a0,a1,......an-1)的结构如图:

一、节点类型描述:

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

也可这样表示:

[cpp] view plaincopy
  1. struct node_t
  2. {
  3. data_t data;
  4. struct node_t *next;
  5. }
  6. typedef struct node_t linknode_t;
  7. typedef struct node_t *linklist_t;

若说明

linknode_t  A;

linklist_t p  = &A;

则结构变量A为所描述的节点,而指针变量P为指向此类型节点的指针(p的值为节点的地址);

这样看来 linknode_t  linklist_t 的作用是一样的,那为什么我们要定义两个数据类型(同一种)呢?主要为了代码的可读性,我们要求标识符要望文识义,便于理解;

1、linknode_t  *pnode  指向一个节点;

2、linklist_t list  指向一个整体

二、头结点 head

        我们在前篇提到的顺序存储线性表,如何表达一个空表{ },是通过list->last = -1来表现的,所谓的空表就是数据域为NULL,而我们的链表有数据域和指针域,我们如何表现空链表呢?这时,就引入了头结点的概念,头结点和其他节点数据类型一样,只是数据域为NULL,head->next = NULL,下面我们看一个创建空链表的函数,如何利用头结点来创建一个空链表:

[cpp] view plaincopy
  1. linklist_t CreateEmptyLinklist()
  2. {
  3. linklist_t list;
  4. list = (linklist_t)malloc(sizeof(linknode_t));
  5. if (NULL != list) {
  6. list->next = NULL;
  7. }
  8. return list;
  9. }

只要头结点,链表就还在!

三、链表基本运算的相关算法

         链表的运算除了上面的创建空链表,还有数据的插入,删除,查找等函数,链表的运算有各种实现方法,如何写出一个高效的,封装性较好的函数是我们要考虑的,比如数据插入函数,我们就要尽可能考虑所有能出现的结果,比如:1)如果需插入数据的链表是个空表;2)所插入的位置超过了链表的长度;如果我们的函数能包含所有能出现的情况,不仅能大大提高我们的开发效率,也会减少代码的错误率。下面,我们来看看下面的这个链表的插入函数的实现:

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

这个插入函数可利用性就非常高。

下面讲一个完整链表代码贴出:

listlink.h

[cpp] view plaincopy
  1. #ifndef _LNK_LIST_H_
  2. #define _LNK_LIST_H_
  3. typedef int data_t;
  4. typedef struct node_t {
  5. data_t data;
  6. struct node_t *next;
  7. } linknode_t, *linklist_t;
  8. linklist_t CreateEmptyLinklist();
  9. void DestroyLinklist(linklist_t list);
  10. void ClearLinklist(linklist_t list);
  11. int EmptyLinklist(linklist_t list);
  12. int LengthLinklist(linklist_t list);
  13. int GetLinklist(linklist_t list, int at, data_t *x);
  14. int SetLinklist(linklist_t list, int at, data_t x);
  15. int InsertLinklist(linklist_t list, int at, data_t x);
  16. int DeleteLinklist(linklist_t list, int at);
  17. linklist_t ReverseLinklist(linklist_t list);
  18. #endif /* _LNK_LIST_H_ */

linklist.c

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "linklist.h"
  4. linklist_t CreateEmptyLinklist()
  5. {
  6. linklist_t list;
  7. list = (linklist_t)malloc(sizeof(linknode_t));
  8. if (NULL != list) {
  9. list->next = NULL;
  10. }
  11. return list;
  12. }
  13. void DestroyLinklist(linklist_t list)
  14. {
  15. if (NULL != list) {
  16. ClearLinklist(list);
  17. free(list);
  18. }
  19. }
  20. void ClearLinklist(linklist_t list)
  21. {
  22. linknode_t *node; /* pointer to the node to be removed */
  23. if (NULL == list) return;
  24. while (NULL != list->next) {
  25. node = list->next;
  26. list->next = node->next;
  27. free(node);
  28. }
  29. return;
  30. }
  31. int LengthLinklist(linklist_t list)
  32. {
  33. int len = 0;
  34. linknode_t *node; //iterate pointer
  35. if (NULL == list) return -1;
  36. node = list->next; // node points to the first data node
  37. while (NULL != node) {
  38. len++;
  39. node = node->next;
  40. }
  41. return len;
  42. }
  43. int EmptyLinklist(linklist_t list)
  44. {
  45. if (NULL != list) {
  46. if (NULL == list->next) {
  47. return 1;
  48. } else {
  49. return 0;
  50. }
  51. } else {
  52. return -1;
  53. }
  54. }
  55. int GetLinklist(linklist_t list, int at, data_t *x)
  56. {
  57. linknode_t *node;   /* used for iteration */
  58. int pos;        /* used for iteration and compare with */
  59. if (NULL == list) return -1;
  60. /* at must >= 0 */
  61. if (at < 0) return -1;
  62. /* start from the first element */
  63. node = list->next;
  64. pos = 0;
  65. while (NULL != node) {
  66. if (at == pos) {
  67. if (NULL != x) {
  68. *x = node->data;
  69. }
  70. return 0;
  71. }
  72. /* move to the next */
  73. node = node->next;
  74. pos++;
  75. }
  76. return -1;
  77. }
  78. int SetLinklist(linklist_t list, int at, data_t x)
  79. {
  80. linknode_t *node; /* used for iteration */
  81. int pos;
  82. int found = 0;
  83. if (!list) return -1;
  84. /* at must >= 0 */
  85. if (at < 0) return -1;
  86. /* start from the first element */
  87. node = list->next;
  88. pos = 0;
  89. while (NULL != node) {
  90. if (at == pos) {
  91. found = 1; /* found the position */
  92. node->data = x;
  93. break;
  94. }
  95. /* move to the next */
  96. node = node->next;
  97. pos++;
  98. }
  99. if (1 == found) {
  100. return 0;
  101. } else {
  102. return -1;
  103. }
  104. }
  105. int InsertLinklist(linklist_t list, int at, data_t x)
  106. {
  107. /*
  108. * node_at and pos_at are used to locate the position of node_at.
  109. * node_prev follows the node_at and always points to previous node
  110. *  of node_at.
  111. * node_new is used to point to the new node to be inserted.
  112. */
  113. linknode_t  *node_prev, *node_at, *node_new;
  114. int     pos_at;
  115. int         found = 0;
  116. if (NULL == list) return -1;
  117. /* at must >= 0 */
  118. if (at < 0) return -1;
  119. node_new = malloc(sizeof(linknode_t));
  120. if (NULL == node_new) {
  121. return -1;
  122. }
  123. node_new->data = x; /* assigned value */
  124. node_new->next = NULL;
  125. node_prev = list;
  126. node_at = list->next;
  127. pos_at = 0;
  128. while (NULL != node_at) {
  129. if (pos_at == at) {
  130. /*
  131. * found the node 'at'
  132. */
  133. found = 1;
  134. break;
  135. }
  136. /* move to the next pos_at */
  137. node_prev = node_at;
  138. node_at = node_at->next;
  139. pos_at++;
  140. }
  141. if (found) {
  142. /* insert */
  143. node_new->next = node_at;
  144. node_prev->next = node_new;
  145. } else {
  146. /*
  147. * If not found, means the provided "at"
  148. * exceeds the upper limit of the list, just
  149. * append the new node to the end of the list.
  150. */
  151. node_prev->next = node_new;
  152. }
  153. return 0;
  154. }
  155. int DeleteLinklist(linklist_t list, int at)
  156. {
  157. /*
  158. * node_at and pos_at are used to locate the position of node_at.
  159. * node_prev follows the node_at and always points to previous node
  160. *  of node_at.
  161. */
  162. linknode_t  *node_prev, *node_at;
  163. int     pos_at;
  164. int         found = 0;
  165. if (!list) return -1;
  166. /* at must >= 0 */
  167. if (at < 0) return -1;
  168. node_prev = list;
  169. node_at = list->next;
  170. pos_at = 0;
  171. while (NULL != node_at) {
  172. if (pos_at == at) {
  173. /*
  174. * found the node 'at'
  175. */
  176. found = 1;
  177. break;
  178. }
  179. /* move to the next pos_at */
  180. node_prev = node_at;
  181. node_at = node_at->next;
  182. pos_at++;
  183. }
  184. if (found) {
  185. /* remove */
  186. node_prev->next = node_at->next;
  187. free(node_at);
  188. return  0;
  189. } else {
  190. return -1;
  191. }
  192. }
  193. linklist_t ReverseLinklist(linklist_t list)
  194. {
  195. linknode_t *node;   /* iterator */
  196. linknode_t *node_prev;  /* previous node of iterator */
  197. linknode_t *node_next;  /* next node of iterator,
  198. * used to backup next of iterator
  199. */
  200. if (NULL == list) return NULL;
  201. node_prev = NULL;
  202. node = list->next;
  203. while (NULL != node) {
  204. /*
  205. * step1: backup node->next
  206. * due to the next of iterator will be
  207. * modified in step2
  208. */
  209. node_next = node->next;
  210. /*
  211. * when iterator reaches the last node
  212. * of original list, make the list head
  213. * point to the last node, so the original
  214. * last one becomes the first one.
  215. */
  216. if (NULL == node_next) {
  217. list->next = node;
  218. }
  219. /*
  220. * step2: reverse the linkage between nodes
  221. * make the node pointer to the previous node,
  222. * not the next node
  223. */
  224. node->next = node_prev;
  225. /*
  226. * step3: move forward
  227. */
  228. node_prev = node;
  229. node = node_next;
  230. }
  231. return list;
  232. }

main.c

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "linklist.h"
  4. int main()
  5. {
  6. int i;
  7. data_t x;
  8. linklist_t p;
  9. p = CreateEmptyLinklist();
  10. data_t a[10] = {1,3,5,7,9,11,13,15,17,19};
  11. for(i = 0;i < 10;i++)
  12. {
  13. InsertLinklist(p,i,a[i]);
  14. }
  15. ReverseLinklist(p);
  16. printf("The length of the list is:%d\n",LengthLinklist(p));
  17. GetLinklist(p,4,&x);
  18. printf("The NO.4 of this list is:%d\n",x);
  19. SetLinklist(p,4,100);
  20. GetLinklist(p,4,&x);
  21. printf("After updating!The No.4 0f this list is:%d\n",x);
  22. DeleteLinklist(p,4);
  23. printf("After updating!The length of the list is:%d\n",LengthLinklist(p));
  24. GetLinklist(p,4,&x);
  25. printf("After updating!The No.4 0f this list is:%d\n",x);
  26. ReverseLinklist(p);
  27. ClearLinklist(p);
  28. if(EmptyLinklist(p))
  29. printf("This list is empty!\n");
  30. DestroyLinklist(p);
  31. printf("This list is destroyed!\n");
  32. return 0;
  33. }

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/list/list2$ ./Test
  2. The length of the list is:10
  3. The NO.4 of this list is:11
  4. After updating!The No.4 0f this list is:100
  5. After updating!The length of the list is:9
  6. After updating!The No.4 0f this list is:9
  7. This list is empty!
  8. This list is destroyed!

Linux C 数据结构---链表(单向链表)相关推荐

  1. 算法与数据结构(part6)--单向链表

    学习笔记,仅供参考,有错必纠 参考自:单链表头指针.头结点.头元结的辨析 文章目录 算法与数据结构–基于python 链表 为啥需要链表 什么是链表 单向链表 什么是单向链表 单列表的操作 节点的实现 ...

  2. 数据结构 (二) ----- 单向链表双向链表

    相关文章: <数据结构 (一) ----- 数据结构基本概念&基于数组实现线性表> <数据结构 (二) ----- 单向链表&双向链表> 文章目录 单链表 一. ...

  3. Java版数据结构之单向链表 新增,有序新增的两种方式,修改和删除(CRUD)

    Java版数据结构之单向链表 CRUD Java版数据结构之单向链表 新增,有序新增的两种方式,修改和删除; 留了一个疑问; 我的代码仓库:https://github.com/zhuangbinan ...

  4. Java版数据结构之单向链表

    Java版数据结构之单向链表 我的代码仓库:https://github.com/zhuangbinan/datastructure package club.zhuangbinan.linkedli ...

  5. 数据结构入门——单向链表

    基本知识点 注:数据结构系列将持续更新,欢迎交流讨论- 单向链表是一种线性结构 优点:作数据的删除.插入简单:缺点:数据查找慢 组成:数据域.后继节点的一个指针域: 单向链表,后继指针指向下一个结点 ...

  6. Day 62 数据结构(单向链表,单向循环链表,双向链表)

    1. 单向链表的设计 例程:创建一个动态单向链表 1.定义链表结点:数据域+指针域 2.定义链表结构体:头结点指针+结点数 3.初始化链表 4.指定位置插入新数据 5.删除指定位置数据 6.获取链表长 ...

  7. 数据结构——求单向链表的倒数第K个节点

    首先,对于链表来说,我们不能像数组一样直接访问,所以我们想到要求倒数第K个节点首先要知道最后一个节点. 然后从最后一个节点往前数K个. 最后得到想要的值. 但是这是不对的,为什么呢?因为题目给出的是单 ...

  8. php mysql 链表_php实现数据结构的单向链表

    啥是单向链表 链表是以链式存储数据的结构,其不需要连续的存储空间,链表中的数据以节点来表示,每个节点由元素(存储数据)和指针(指向后继节点)组成. 单向链表(也叫单链表)是链表中最简单的一种形式,每个 ...

  9. 数据结构:单向链表(SingleLinkedList)删除某一节点

    定义一个简单的单向链表节点 public class ListNode {int value;ListNode next;ListNode(int x) { val = x;} } 思路一 //将前一 ...

  10. 【数据结构】单向链表的原理及实现

    1.什么是单链表 链表里的数据是以节点的方式表示的,每一个结点的组成是由:元素+指针来组成的,元素就是存储数据里的存储单元,指针就是用来连接每一个结点的地址数据.这个以结点的序列来表示线性表被称作为单 ...

最新文章

  1. 必看2021年80后夫妻同时过信息系统项目管理师
  2. JZOJ 5490. 【清华集训2017模拟11.28】图染色
  3. 斗罗大陆html5游戏在线玩,斗罗大陆H5在线玩
  4. OpenCV里IplImage的widthStep参数 和width参数
  5. ie11不兼容 html编辑器,ie11兼容性视图护驾浏览网页
  6. PHP笔记-所有错误统一输出404页面(详细错误日志输出,提高安全性)
  7. getallheaders函数在服务器报500错误_「干货」服务器性能优化的8种常用方法
  8. 计算机数学英语基础,计算机数学基础教程 2012年版
  9. centos下smartctl安装配置(硬盘S.M.A.R.T信息及坏块检测命令)
  10. 用ffmpeg批量转换WAV文件采样率
  11. 什么是PID,PID的作用。
  12. 数学建模(三)SARS的传播(03年A题)
  13. python如何开发网站_如何用Python写一个小网站?
  14. 如何把标签输出为PDF文件
  15. 帝国Cms7.5后台getshell | (CVE-2018-18086)漏洞复现
  16. java百万级大数据量导出
  17. 使用setViewControllers实现一些不同寻常的跳转
  18. 【java】Eclipse使用
  19. 我的MFC/C++学习笔记 http://blog.bccn.net/CrystalFan/6909
  20. 吃自助最高境界:扶墙进,扶墙出

热门文章

  1. [转]C# WInForm 无框窗体移动
  2. 关系型数据库的核心单元是_核中的数据关系
  3. 足球预测_预测足球热
  4. html制作彩虹_制作彩虹
  5. 静态变数和非静态变数_统计资料:了解变数
  6. 移动平均线ma分析_使用动态移动平均线构建交互式库存量和价格分析图
  7. 描述符、迭代器、生成器
  8. 03JavaScript程序设计修炼之道-2019-06-20_20-31-49
  9. webapi返回字符串,解决自动加双引号或下划线的问题
  10. 使用 VS 附加到进程 调试发布的网站