• 单链表---结构体实现


    定义

    链表称为线性表的链式存储,顺序表逻辑上相邻的数据,存储位置也相邻。链表逻辑上相邻的数据,存储位置是随机分布在内存的各个位置上的。

    对于每一个结点,定义的结构体是:

    1. typedef struct _LinkNode
    2. {
    3. int date; //数据域,存储数据,这里是int类型的数据
    4. struct _LinkNode* next; // 指针域,指向了后继元素(下一个结点)的地址
    5. }LinkNode , LinkList; //两个别名的作用是一模一样的,只是为了区分头结点和结点

     链表的初始化

    我们用一个结构体指针L来指向链表,最开始是带一个头结点的空链表,头结点的数据域一般赋值为-1,也可以存储着链表的长度。头结点的指针域指针第一个元素结点,最开始是没有元素结点的空链表(有头结点),如图:

    代码实现

    1. bool initLinkList(LinkList*& L) //参数是对结构体指针的引用
    2. {
    3. L = new LinkList; //也可以写成 L = new LinkNode ;
    4. if (!L)
    5. {
    6. return false; // 申请内存失败,返回了一个空指针
    7. }
    8. L->next = NULL;
    9. return true;
    10. }

    链表的插入

    头插法

    将新结点插到头结点的后面,有两种形式,一是链表为空,二是链表中已有元素结点,如图

    你可以分情况,不过也可以不用,例如:

    1. bool insertLinkList(LinkList*& L, LinkNode *node) //node指向要添加的结点
    2. {
    3. if (!L || !node) return false;
    4. node->next = L->next; //包含两种情况
    5. L->next = node;
    6. return true;
    7. }

    尾插法

    尾插,顾名思义就是在最后一个结点插入之后,也分两种情况,一是链表为空,二是链表中已有元素结点,第一种情况的图和头插法的第一种情况的图一样,就是插入方式不同,以下是第二种情况的图:

    我们只需要找到 next 指向 NULL 的这个结点就好了

    1. bool LinkListinsert(LinkList*& L, LinkNode *node)
    2. {
    3. if (!L || !node) return false;
    4. LinkNode* p = NULL;
    5. p = L;
    6. while (p->next != NULL) //如果是空链表,p == L,所以是包含两种情况
    7. {
    8. p = p->next;
    9. }
    10. node->next = p->next;
    11. p->next = node;
    12. return true;
    13. }

    指定位置插入

    在第 i 个结点前插入,那么我们就必须有个临时指针指向第 i-1 个结点

    1. bool InsertLink(LinkList*& L, int i, int e) // 这里也可以用之前的LinkNode *node参数,不过可能i不合法
    2. {
    3. if (!L) return false;
    4. LinkNode* p = L , *s = NULL ;
    5. int j = 0;
    6. while (p && j < i - 1) // 指向第 i-1 个结点
    7. {
    8. p = p->next;
    9. j++;
    10. }
    11. //如果在第0个位置之前插入(i<1) || 如果在最后一个结点+2的位置之前插入(i>n+1,n为链表长度)---都是非法的
    12. if (i < 1 || !p) return false;
    13. s = new LinkNode;
    14. s->date = e;
    15. s->next = p->next;
    16. p->next = s;
    17. return true;
    18. }

    遍历链表(打印链表)

    1. bool PrintLinkList(LinkList *& L)
    2. {
    3. if(!L) return false;
    4. LinkNode* p = L->next; //首先指向第一个结点,如果为空链表就会指向 NULL
    5. while (p != NULL) // 直到指向最后一个结点的 next 指针(NULL
    6. {
    7. printf("%d ", p->date);
    8. p = p->next;
    9. }
    10. cout << endl;
    11. return true;
    12. }

    遍历链表,也可以用来求链表的长度,代码与这个类似。

    链表的获取

    获取链表第 i 个结点的数据域。

    1. bool GetElemLink(LinkList*& L, int i, int &e) //e返回获取的数据域
    2. {
    3. LinkNode* p = L;
    4. int j = 0;
    5. while ( p!=NULL && j < i)
    6. {
    7. p = p->next;
    8. j++;
    9. }
    10. if (p==NULL || i < 1) //i是非法的:i > n(n为链表长度,例如:获取第n+1个结点的数据域)或者是
    11. i < 1(例如:获取了第0个结点的数据域)
    12. {
    13. return false;
    14. }
    15. e = p->date;
    16. return true;
    17. }

    链表的查找

    查找我们输入的元素在链表中出现的第一个位置。

    1. bool FindElem(LinkList* L, int e,int &index) // index返回位置
    2. {
    3. if (!L || !L->next) return false; //空链表
    4. LinkNode* p = L->next; // 现在指向第一个结点
    5. int j = 1;
    6. while (p != NULL && p->date != e)
    7. {
    8. p = p->next;
    9. j++;
    10. }
    11. if (p == NULL)
    12. return false;
    13. else
    14. {
    15. index = j;
    16. return true;
    17. }
    18. }

    链表的删除

    删除第 i 个结点,i 可能不合法,注意不合法的原因。

    1. bool DeleteLinkNode(LinkList*& L, int i)
    2. {
    3. LinkNode* p = NULL , *q = NULL;
    4. p = L;
    5. int j = 0;
    6. while ( p->next!=NULL && j < i - 1)
    7. {
    8. p = p->next;
    9. j++;
    10. }
    11. // 指向第 i-1 个结点
    12. if (i < 1 || p->next == NULL) //i非法,删除第0个结点(i<1) 或者是 删除第n+1个结点(i>n,n为链表的长度)
    13. return false;
    14. else
    15. {
    16. q = p->next;
    17. p->next = q->next ;
    18. delete q;
    19. return true;
    20. }
    21. }

    链表的销毁(清空)

    销毁就是要归还所申请的内存,删除某个结点时,一定要保存下一个结点的地址。简要解释:先保存第一个结点的地址,随后删除头结点(也申请了空间),再保存第二个结点的地址,删除第一个结点,再保存第三个结点……以此类推,可以将全部结点删除。

    1. void DestoryLink(LinkList*& L)
    2. {
    3. LinkNode* p = NULL, * q = NULL;
    4. p = L;
    5. while (p != NULL)
    6. {
    7. q = p->next;
    8. //cout << "删除元素" << p->date << endl;
    9. delete p;
    10. p = q;
    11. }
    12. }

    总代码

    以下是全部代码,链表的函数有不同的实现方法。也许我的代码和别人有些差异,所以可以看看 main 函数中是怎样的。

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include <iostream>
    3. using namespace std;
    4. typedef struct _LinkNode
    5. {
    6. int date; //数据域
    7. struct _LinkNode* next; // 指针域
    8. }LinkNode , LinkList;
    9. //初始化链表
    10. bool initLinkList(LinkList*& L)
    11. {
    12. L = new LinkList;
    13. if (!L)
    14. {
    15. return false;
    16. }
    17. L->next = NULL;
    18. return true;
    19. }
    20. //前插法
    21. bool insertLinkList(LinkList*& L, LinkNode *node)
    22. {
    23. if (!L || !node) return false;
    24. node->next = L->next;
    25. L->next = node;
    26. return true;
    27. }
    28. //尾插法
    29. bool LinkListinsert(LinkList*& L, LinkNode *node)
    30. {
    31. if (!L || !node) return false;
    32. LinkNode* p = NULL;
    33. p = L;
    34. while (p->next != NULL)
    35. {
    36. p = p->next;
    37. }
    38. node->next = p->next;
    39. p->next = node;
    40. return true;
    41. }
    42. //在第i个结点前插入---指定位置插入
    43. bool InsertLink(LinkList*& L, int i, int e)
    44. {
    45. if (!L) return false;
    46. LinkNode* p = L , *s = NULL ;
    47. int j = 0;
    48. while (p && j < i - 1)
    49. {
    50. p = p->next;
    51. j++;
    52. }
    53. //如果在第0个位置之前插入(i<1) || 如果在最后一个结点+2的位置之前插入(i>n+1,n为链表长度)---都是非法的
    54. if (i < 1 || !p) return false;
    55. s = new LinkNode;
    56. s->date = e;
    57. s->next = p->next;
    58. p->next = s;
    59. return true;
    60. }
    61. //打印链表
    62. void PrintLinkList(LinkList *& L)
    63. {
    64. LinkNode* p = L->next;
    65. while (p != NULL)
    66. {
    67. printf("%d ", p->date);
    68. p = p->next;
    69. }
    70. cout << endl;
    71. }
    72. //获取第i个结点的数据元素
    73. bool GetElemLink(LinkList*& L, int i, int &e)
    74. {
    75. LinkNode* p = L;
    76. int j = 0;
    77. while ( p!=NULL && j < i)
    78. {
    79. p = p->next;
    80. j++;
    81. }
    82. if (p==NULL || i < 1)
    83. {
    84. return false;
    85. }
    86. e = p->date;
    87. return true;
    88. }
    89. //在链表中查找元素,并且用index返回位置
    90. bool FindElem(LinkList* L, int e,int &index)
    91. {
    92. if (!L || !L->next) return false;
    93. LinkNode* p = L->next;
    94. int j = 1;
    95. while (p && p->date != e)
    96. {
    97. p = p->next;
    98. j++;
    99. }
    100. if (p == NULL)
    101. return false;
    102. else
    103. {
    104. index = j;
    105. return true;
    106. }
    107. }
    108. //删除第i个结点
    109. bool DeleteLinkNode(LinkList*& L, int i)
    110. {
    111. LinkNode* p = NULL , *q = NULL;
    112. p = L;
    113. int j = 0;
    114. while ( p->next!=NULL && j < i - 1)
    115. {
    116. p = p->next;
    117. j++;
    118. }
    119. if (i < 1 || p->next == NULL) //删除第0个结点(i<1) 或者是 删除第n+1个结点(i>n,n为链表的长度)
    120. return false;
    121. else
    122. {
    123. q = p->next;
    124. p->next = q->next ;
    125. delete q;
    126. return true;
    127. }
    128. }
    129. //清空(销毁)链表
    130. void DestoryLink(LinkList*& L)
    131. {
    132. LinkNode* p = NULL, * q = NULL;
    133. p = L;
    134. while (p != NULL)
    135. {
    136. q = p->next;
    137. //cout << "删除元素" << p->date << endl;
    138. delete p;
    139. p = q;
    140. }
    141. }
    142. int main(void)
    143. {
    144. LinkList* L = NULL; //指向表头(头结点)的指针
    145. LinkNode* e = NULL; //指向结点的指针
    146. initLinkList(L);
    147. int elem = 0; //结点的数据元素
    148. int i; //第几个结点
    149. int n = 0; //输入的个数
    150. int flag = -1;
    151. while (flag != 0)
    152. {
    153. cout << "1.前插链表" << endl
    154. << "2.尾插链表" << endl
    155. << "3.指定位置前插入" << endl
    156. << "4.打印链表" << endl
    157. << "5.获取第i个结点的数据" << endl
    158. << "6.查找元素" << endl
    159. <<"7.删除元素"<<endl
    160. << "0.退出(销毁链表)" << endl;
    161. cout << "请选择:";
    162. cin >> flag;
    163. switch (flag)
    164. {
    165. case 1:
    166. cout << "请输入前插法插入链表的个数";
    167. cin >> n;
    168. cout << "请依次输入" << n << "个数:";
    169. while (n--)
    170. {
    171. e = new LinkNode;
    172. cin >> e->date;
    173. insertLinkList(L, e);
    174. }
    175. break;
    176. case 2:
    177. cout << "请输入尾插入链表的个数";
    178. cin >> n;
    179. cout << "请依次输入" << n << "个数:";
    180. while (n--)
    181. {
    182. e = new LinkNode;
    183. cin >> e->date;
    184. LinkListinsert(L, e);
    185. }
    186. break;
    187. case 3:
    188. cout << "请输入插入的位置和元素:";
    189. cin >> i >> elem;
    190. InsertLink(L, i, elem);
    191. break;
    192. case 4:
    193. PrintLinkList(L);
    194. break;
    195. case 5:
    196. cout << "请输入要获取的结点i:";
    197. cin >> i;
    198. if (GetElemLink(L, i, elem))
    199. {
    200. cout << "第" << i << "个结点的值为" << elem << endl;
    201. }
    202. else
    203. {
    204. cout << "获取元素失败" << endl;
    205. }
    206. break;
    207. case 6:
    208. cout << "请输入要查找的元素:";
    209. cin >> elem;
    210. if (FindElem(L, elem, i))
    211. {
    212. cout << "找到了,此元素的位置是" << i << endl;
    213. }
    214. else
    215. {
    216. cout << "表中无此值" << endl;
    217. }
    218. break;
    219. case 7:
    220. cout << "请输入要删除的结点i:";
    221. cin >> i;
    222. if (DeleteLinkNode(L, i))
    223. {
    224. cout << "删除成功" << endl;
    225. }
    226. else
    227. {
    228. cout << "删除失败" << endl;
    229. }
    230. break;
    231. case 0:
    232. DestoryLink(L);
    233. break;
    234. default:
    235. cout << "输入非法! " << endl;
    236. break;
    237. }
    238. }
    239. return 0;
    240. }

    这里也可以将代码复制,自己调试测试一下,删除某些代码,看看会有什么影响,更能理解。

  • 相关阅读:
    Python数据类型:数字
    如何优雅的实现无侵入性参数校验之spring-boot-starter-validation
    JS 数据结构:队列
    IDEA快捷键
    数学问题-反射定律&折射定律的向量形式推导
    一起学习 学习二叉树
    计算机网络基础(二):物理层、数据链路层及网络层
    Mockito搭配junit单元测试
    复杂AB实验
    基于单片机的水位检测系统仿真设计
  • 原文地址:https://blog.csdn.net/qq_66805048/article/details/133798211