双向链表的存储设计与实现(C语言版本)

╰半夏微凉° 2022-05-24 01:06 191阅读 0赞

双向链表的插入操作:

70

双向链表的删除操作:

70 1

  1. //DLinkList.h
  2. #ifndef _DLINKLIST_H_
  3. #define _DLINKLIST_H_
  4. //双向链表的存储结构
  5. //结点中包含后继结点的地址的指针域可以理解为指向下一个结构体(结点)
  6. //(这里不包含数据域,是实现了 链表的api(链表的算法) 和 具体的数据分离)
  7. typedef struct _tag_DLinkListNode
  8. {
  9. struct _tag_DLinkListNode *next;//指向后继的next指针
  10. struct _tag_DLinkListNode *pre;//指向前驱的pre指针
  11. }DLinkListNode;
  12. //为void 再重新多取一个名字,DLinkList等价于void
  13. //typedef + 已有的数据类型+新的数据类型(自己取的新名字)
  14. typedef void DLinkList;
  15. //创建并且返回一个空的双向链表
  16. DLinkList* DLinkList_Create();
  17. //销毁一个双向链表list
  18. void DLinkList_Destroy(DLinkList* list);
  19. //将一个双向链表list中的所有元素清空, 循环链表回到创建时的初始状态
  20. void DLinkList_Clear(DLinkList* list);
  21. //返回一个双向链表list中的元素个数
  22. int DLinkList_Length(DLinkList* list);
  23. //向一个双向链表list的pos位置处插入元素
  24. int DLinkList_Insert(DLinkList* list, DLinkList* node, int pos);
  25. //获取一个双向链表list中pos位置处的元素
  26. DLinkListNode* DLinkList_Get(DLinkList* list, int pos);
  27. //删除一个双向链表list中pos位置处的元素,返回值为被删除的元素,NULL表示删除失败
  28. DLinkListNode* DLinkList_Delete(DLinkList* list, int pos);
  29. //add
  30. //直接指定删除双向链表中的某个数据元素
  31. DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node);
  32. //将游标重置指向双向链表中的第一个数据元素
  33. DLinkListNode* DLinkList_SliderReset(DLinkList* list);
  34. //双向链表 获取当前游标指向的数据元素
  35. DLinkListNode* DLinkList_SliderCurrent(DLinkList* list);
  36. //将游标移动指向到双向链表中的下一个数据元素
  37. DLinkListNode* DLinkList_SliderNext(DLinkList* list);
  38. //将游标移动指向双向链表中的前一个数据元素
  39. DLinkListNode* DLinkList_SliderPre(DLinkList *list);
  40. #endif
  41. //DLinkList.c
  42. #include <stdlib.h>
  43. #include <string.h>
  44. #include <stdio.h>
  45. #include "DLinkList.h"
  46. //定义双向链表的头结点 双向链表头结点:表示链表中第一个节点,包含指向第一个数据元素的指针以及链表自身的一些信息
  47. //这样能把所有结点串起来
  48. typedef struct _tag_DLinkList
  49. {
  50. DLinkListNode header; //要有个头指针---指向头结点的指针
  51. DLinkListNode *slider;//在双向链表中可以定义一个“当前指针”,这个指针通常称为游标,可以通过游标来遍历链表中所有元素
  52. int length;//链表的长度
  53. }TDLinkList;
  54. //创建并且返回一个空的双向链表
  55. DLinkList* DLinkList_Create()
  56. {
  57. //1 申请动态内存空间
  58. TDLinkList *tmp = (TDLinkList *)malloc(sizeof(TDLinkList));
  59. if (NULL == tmp)
  60. {
  61. printf("func err malloc:%d\n");
  62. return NULL;
  63. }
  64. //2 让开辟的内存 完成链式线性表初始化
  65. memset(tmp, 0, sizeof(TDLinkList));
  66. //3 链表的初始化
  67. tmp->header.next = NULL;
  68. tmp->header.pre = NULL;
  69. tmp->slider = NULL;
  70. tmp->length = 0;
  71. return tmp;
  72. }
  73. //销毁一个双向链表list
  74. //由于数据元素节点的生命周期不是由链表算法管理的,所以只需要销毁申请的头节点元素即可
  75. void DLinkList_Destroy(DLinkList* list)
  76. {
  77. //1 缓存下来 进行操作
  78. TDLinkList *tmp = NULL;
  79. tmp = (TDLinkList *)list;
  80. if (NULL == list)
  81. {
  82. printf("func err DLinkList_Destroy()\n");
  83. }
  84. //2 释放头结点空间
  85. if (tmp != NULL)
  86. {
  87. free(tmp);
  88. }
  89. }
  90. //将一个双向链表list中的所有元素清空, 双向链表回到创建时的初始状态
  91. void DLinkList_Clear(DLinkList* list)
  92. {
  93. //1 缓存下来 进行操作
  94. TDLinkList *tmp = NULL;
  95. tmp = (TDLinkList *)list;
  96. if (NULL == list)
  97. {
  98. printf("func err DLinkList_Clear()\n");
  99. }
  100. //2 清空链表
  101. tmp->header.next = NULL;
  102. tmp->header.pre = NULL;
  103. tmp->slider = NULL;
  104. tmp->length = 0;
  105. }
  106. //返回一个双向链表list中的元素个数
  107. int DLinkList_Length(DLinkList* list)
  108. {
  109. int ret = 0;
  110. //1 缓存下来 进行操作
  111. TDLinkList *tmp = NULL;
  112. tmp = (TDLinkList *)list;
  113. if (NULL == list)
  114. {
  115. ret = -1;
  116. printf("func err DLinkList_Length():%d\n", ret);
  117. return ret;
  118. }
  119. ret = tmp->length;
  120. return ret;
  121. }
  122. //向一个双向链表list的pos位置处插入元素
  123. int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos)
  124. {
  125. int ret = 0;
  126. //1 缓存下来 进行操作
  127. TDLinkList *tmp = NULL;
  128. tmp = (TDLinkList *)list;
  129. //辅助指针 用来遍历当前指针位置
  130. DLinkListNode *pCur = NULL;
  131. //辅助指针 用来缓存当前指针下移位置
  132. DLinkListNode *pNext = NULL;
  133. if (NULL == list || NULL == node || pos < 0)
  134. {
  135. ret = -1;
  136. printf("func err (NULL == list || NULL == node || pos < 0):%d\n", ret);
  137. return ret;
  138. }
  139. //注意:容错修正,假如链表当前长度为5,你插入pos位置是10,这个时候可以做容错修正,直接修正为尾插法
  140. if (pos > tmp->length)
  141. {
  142. pos = tmp->length;
  143. }
  144. //1 当前指针 初始化 指向 头结点
  145. pCur = &(tmp->header);
  146. //2 进行遍历 找到插入位置
  147. for (int i = 0; i < pos; i++)
  148. {
  149. pCur = pCur->next;
  150. }
  151. //3 进行插入操作
  152. //进行缓存pCur next域
  153. pNext = pCur->next;
  154. //插入操作
  155. pCur->next = node;//1
  156. node->next = pNext;//2
  157. if (pNext != NULL)
  158. {
  159. pNext->pre = node;//3 若是尾插法,就不需要这部操作
  160. }
  161. node->pre = NULL;//若是头插法,需要这部操作,如图1 4
  162. if (pCur !=(DLinkListNode *)tmp)//若不是头插法,普通插入如图2 3情况
  163. {
  164. node->pre = pCur;//4
  165. }
  166. //若第一次插入结点 让游标指向0号结点
  167. if (tmp->length == 0)
  168. {
  169. tmp->slider = node;
  170. }
  171. //4 链表长度++
  172. tmp->length++;
  173. return ret;
  174. }
  175. //获取一个双向链表list中pos位置处的元素
  176. DLinkListNode* DLinkList_Get(DLinkList* list, int pos)
  177. {
  178. //1 缓存下来 进行操作
  179. TDLinkList *tmp = (TDLinkList *)list;
  180. //辅助指针 用来遍历当前指针位置
  181. DLinkListNode *pCur = NULL;
  182. if (NULL == list || pos < 0)
  183. {
  184. printf("func err DLinkList_Get\n");
  185. return NULL;
  186. }
  187. //2 当前指针 初始化 指向 头结点
  188. pCur = &(tmp->header);
  189. //3 搜索要获得的结点
  190. for (int i = 0; i < pos; i++)
  191. {
  192. pCur = pCur->next;
  193. }
  194. return pCur->next;
  195. }
  196. //删除一个循环链表list中pos位置处的元素,返回值为被删除的元素,NULL表示删除失败
  197. DLinkListNode* DLinkList_Delete(DLinkList* list, int pos)
  198. {
  199. //1 缓存下来 进行操作
  200. TDLinkList *tmp = (TDLinkList *)list;
  201. //辅助指针 用来遍历当前指针位置
  202. DLinkListNode *pCur = NULL;
  203. //辅助指针 用来缓存删除结点下一元素
  204. DLinkListNode *pAfter = NULL;
  205. //辅助指针 用来缓存删除元素
  206. DLinkListNode *DeletNode = NULL;
  207. if (NULL == list || pos < 0)
  208. {
  209. printf("func err DLinkList_Delete\n");
  210. return NULL;
  211. }
  212. //删除位置的pos点不能大于链表长度 做一个容错修正
  213. //当删除位置pos大于双向链表长度,就尾部删除
  214. if (pos > DLinkList_Length(tmp))
  215. {
  216. pos = tmp->length;
  217. }
  218. //2 当前指针 初始化 指向 头结点
  219. pCur = &(tmp->header);
  220. //3 搜索要获得的结点
  221. for (int i = 0; i < pos; i++)
  222. {
  223. pCur = pCur->next;
  224. }
  225. //4 缓存结点位置
  226. DeletNode = pCur->next;
  227. pAfter = DeletNode->next;
  228. //5 开启删除操作 分三种情况
  229. pCur->next = pAfter;//普通情况1
  230. if (pAfter !=NULL)
  231. {
  232. pAfter->pre = pCur;//普通情况2
  233. }
  234. DeletNode->pre = NULL;//第三种情况需要置空 第二种已经自己置空
  235. //6 链表长度--
  236. tmp->length--;
  237. //7 若删除元素为游标所指元素 需要后移
  238. if (tmp->slider == DeletNode)
  239. {
  240. tmp->slider = pAfter;
  241. }
  242. //8 若删除元素后链表长度为0
  243. if (tmp->length == 0)
  244. {
  245. tmp->header.next = NULL;
  246. tmp->header.pre = NULL;
  247. tmp->slider = NULL;
  248. }
  249. return DeletNode;
  250. }
  251. //add
  252. //根据结点 直接指定删除链表中的某个数据元素
  253. DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node)
  254. {
  255. //1 缓存下来 进行操作
  256. TDLinkList *tmp = (TDLinkList *)list;
  257. //辅助指针 用来遍历当前指针位置
  258. DLinkListNode *pCur = NULL;
  259. //辅助指针 用来缓存删除元素
  260. DLinkListNode *DeletNode = NULL;
  261. if (NULL == list || 0 == node)
  262. {
  263. printf("func err DLinkList_DeleteNode\n");
  264. return NULL;
  265. }
  266. //2 当前指针 初始化 指向 头结点
  267. pCur = &(tmp->header);
  268. //3 搜索要获得的结点
  269. int i;
  270. for ( i = 0; i < tmp->length; i++)
  271. {
  272. if (pCur->next == node)//根据删除结点搜索到要删除位置
  273. {
  274. DeletNode = pCur->next;//缓存删除元素
  275. break;//这里搜索到要删除的结点,结束循环,否则会报错
  276. }
  277. pCur = pCur->next;
  278. }
  279. //4 根据pos位置删除元素
  280. if (DeletNode != NULL)
  281. {
  282. DLinkList_Delete(tmp,i);
  283. }
  284. return DeletNode;
  285. }
  286. //将游标重置指向链表中的第一个数据元素
  287. DLinkListNode* DLinkList_SliderReset(DLinkList* list)
  288. {
  289. //1 缓存下来 进行操作
  290. TDLinkList *tmp = (TDLinkList *)list;
  291. //辅助指针 用来缓存重置游标位置
  292. DLinkListNode *tmpReset = NULL;
  293. if (NULL == list)
  294. {
  295. printf("func err DLinkList_SliderReset\n");
  296. return NULL;
  297. }
  298. if (tmp!=NULL)
  299. {
  300. tmp->slider = tmp->header.next;//重置指向第一个数据元素
  301. tmpReset = tmp->slider;//缓存游标位置
  302. }
  303. return tmpReset;
  304. }
  305. //获取当前游标指向的数据元素
  306. DLinkListNode* DLinkList_SliderCurrent(DLinkList* list)
  307. {
  308. //1 缓存下来 进行操作
  309. TDLinkList *tmp = (TDLinkList *)list;
  310. //辅助指针 用来缓存当前游标位置
  311. DLinkListNode *tmpCur = NULL;
  312. if (NULL == list)
  313. {
  314. printf("func err DLinkList_SliderCurrent\n");
  315. return NULL;
  316. }
  317. if (tmp!=NULL)
  318. {
  319. tmpCur = tmp->slider;//缓存当前游标位置
  320. }
  321. return tmpCur;
  322. }
  323. //将游标移动指向到链表中的下一个数据元素
  324. DLinkListNode* DLinkList_SliderNext(DLinkList* list)
  325. {
  326. //1 缓存下来 进行操作
  327. TDLinkList *tmp = (TDLinkList *)list;
  328. //辅助指针 用来缓存游标位置
  329. DLinkListNode *tmpNext = NULL;
  330. if (NULL == list)
  331. {
  332. printf("func err DLinkList_SliderNext\n");
  333. return NULL;
  334. }
  335. if (tmp != NULL)
  336. {
  337. tmpNext = tmp->slider;//缓存当前游标位置
  338. tmp->slider = tmpNext->next;//将游标下移
  339. }
  340. return tmpNext;
  341. }
  342. //将游标移动指向双向链表中的前一个数据元素
  343. DLinkListNode* DLinkList_SliderPre(DLinkList *list)
  344. {
  345. //1 缓存下来 进行操作
  346. TDLinkList *tmp = (TDLinkList *)list;
  347. //辅助指针 用来缓存游标位置
  348. DLinkListNode *tmpPre = NULL;
  349. if (NULL == list)
  350. {
  351. printf("func err DLinkList_SliderPre\n");
  352. return NULL;
  353. }
  354. if (tmp != NULL)
  355. {
  356. tmpPre = tmp->slider;//缓存当前游标位置
  357. tmp->slider = tmpPre->pre;//将游标上移
  358. }
  359. return tmpPre;
  360. }
  361. //双向链表的设计与实现测试框架
  362. //text.c
  363. #include <stdlib.h>
  364. #include <string.h>
  365. #include <stdio.h>
  366. #include "DLinkList.h"//c里面.h和.cpp没有差别 但是c++里如果模块化编程,两个头文件都必须包含进来
  367. //业务结点
  368. typedef struct _tag_Teacher
  369. {
  370. DLinkListNode node;//包含底层结点
  371. //业务域
  372. int age;
  373. }Teacher;
  374. int main()
  375. {
  376. int ret = 0;
  377. DLinkList *dlist = NULL;
  378. Teacher t1, t2, t3, t4;
  379. t1.age = 1;
  380. t2.age = 2;
  381. t3.age = 3;
  382. t4.age = 4;
  383. //1 创建并且返回一个空的双向链表
  384. dlist = DLinkList_Create();
  385. if (NULL == dlist)
  386. {
  387. ret = -1;
  388. printf("func err DLinkList_Create:%d\n", ret);
  389. return ret;
  390. }
  391. //2 向一个双向链表list的pos位置处插入元素
  392. ret = DLinkList_Insert(dlist, (DLinkListNode *)&t1, DLinkList_Length(dlist));
  393. if (ret != 0)
  394. {
  395. ret = -2;
  396. printf("func err DLinkList_Insert:%d\n", ret);
  397. return ret;
  398. }
  399. ret = DLinkList_Insert(dlist, (DLinkListNode *)&t2, DLinkList_Length(dlist));
  400. ret = DLinkList_Insert(dlist, (DLinkListNode *)&t3, DLinkList_Length(dlist));
  401. ret = DLinkList_Insert(dlist, (DLinkListNode *)&t4, DLinkList_Length(dlist));
  402. //返回一个双向链表list中的元素个数
  403. ret = DLinkList_Length(dlist);
  404. printf("%d ",ret);
  405. printf("\n========================我是分界线====================\n");
  406. //遍历双向链表
  407. for (int i = 0; i < DLinkList_Length(dlist); i++)
  408. {
  409. //获取游标所指元素,然后游标下移
  410. //双向链表 获取当前游标指向的数据元素
  411. Teacher *tmp = (Teacher *)DLinkList_SliderNext(dlist);
  412. if (NULL == tmp)
  413. {
  414. ret = -3;
  415. printf("func err DLinkList_SliderNext:%d\n", ret);
  416. return ret;
  417. }
  418. printf("%d ",tmp->age);
  419. }
  420. printf("\n========================我是分界线====================\n");
  421. 将一个双向链表list中的所有元素清空, 循环链表回到创建时的初始状态
  422. //DLinkList_Clear(dlist);
  423. 返回一个双向链表list中的元素个数
  424. //ret = DLinkList_Length(dlist);
  425. //printf("%d ", ret);
  426. //再一次 遍历双向链表
  427. for (int i = 0; i < DLinkList_Length(dlist); i++)
  428. {
  429. //获取一个双向链表list中pos位置处的元素
  430. //DLinkListNode* DLinkList_Get(DLinkList* list, int pos);
  431. Teacher *tmp = (Teacher *)DLinkList_Get(dlist,i);
  432. if (NULL == tmp)
  433. {
  434. ret = -4;
  435. printf("func err DLinkList_SliderCurrent:%d\n", ret);
  436. return ret;
  437. }
  438. printf("%d ", tmp->age);
  439. }
  440. printf("\n========================我是分界线====================\n");
  441. //将游标重置指向双向链表中的第一个数据元素
  442. //DLinkListNode* DLinkList_SliderReset(DLinkList* list);
  443. Teacher *tmp1 = (Teacher *)DLinkList_SliderReset(dlist);
  444. printf("%d ", tmp1->age);
  445. Teacher *tmp2 = (Teacher *)DLinkList_SliderNext(dlist);
  446. printf("%d ", tmp2->age);
  447. //直接指定删除双向链表中的某个数据元素
  448. //DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node);
  449. tmp2 = (Teacher *)DLinkList_DeleteNode(dlist, (DLinkListNode*)tmp2);
  450. printf("%d ", tmp2->age);
  451. //删除一个双向链表list中pos位置处的元素,返回值为被删除的元素,NULL表示删除失败
  452. //DLinkListNode* DLinkList_Delete(DLinkList* list, int pos);
  453. tmp2 = (Teacher *)DLinkList_Delete(dlist, 1);
  454. printf("%d ", tmp2->age);
  455. //将游标移动指向双向链表中的前一个数据元素
  456. //DLinkListNode* DLinkList_SliderPre(DLinkList *list);
  457. tmp2 = (Teacher *)DLinkList_SliderPre(dlist);
  458. printf("%d ", tmp2->age);
  459. DLinkList_Destroy(dlist);
  460. system("pause");
  461. return 0;
  462. }

双向链表

1、基本概念



















单链表的结点都只有一个指向下一个结点的指针

单链表的数据元素无法直接访问其前驱元素

逆序访问单链表中的元素是极其耗时的操作!

len = LinkList_Length(list);

for (i=len-1; len>=0; i++) //O(n)

{

LinkListNode *p = LinkList_Get(list, i); //O(n)

//访问数据元素p中的元素

//

}

双向链表的定义

在单链表的结点中增加一个指向其前驱的pre指针

双向链表拥有单链表的所有操作

创建链表

销毁链表

获取链表长度

清空链表

获取第pos个元素操作

插入元素到位置pos

删除位置pos处的元素

2、设计与实现




























插入操作




删除操作

双向链表的新操作

获取当前游标指向的数据元素

将游标重置指向链表中的第一个数据元素

将游标移动指向到链表中的下一个数据元素

将游标移动指向到链表中的上一个数据元素

直接指定删除链表中的某个数据元素

DLinkListNode DLinkList_DeleteNode(DLinkList list, DLinkListNode node);

DLinkListNode DLinkList_Reset(DLinkList list);

DLinkListNode DLinkList_Current(DLinkList list);

DLinkListNode DLinkList_Next(DLinkList list);

DLinkListNode DLinkList_Pre(DLinkList* list);

//大家一定要注意:教科书不会告诉你项目上如何用;哪些点是项目的重点;

做一个企业级的财富库,完成你人生开发经验的积累,是我们的学习重点,要注意!

3、优点和缺点










优点:双向链表在单链表的基础上增加了指向前驱的指针

功能上双向链表可以完全取代单链表的使用

循环链表的Next,Pre和Current操作可以高效的遍历链表中的所有元素

缺点:代码复杂

发表评论

表情:
评论列表 (有 0 条评论,191人围观)

还没有评论,来说两句吧...

相关阅读