1 离散存储【链表】(我们搞底层的开发,类似于SUN公司的类)

1.1 定义:

  1. n个节点离散分配
  2. 彼此通过指针相连
  3. 每个节点只有一个前驱节点,每个节点只有一个后续节点
  4. 首节点没有前驱节点,尾节点没有后续节点。

    1.2 链表的优缺点:

    1.2.1 优点:

  5. 空间没有限制

  6. 插入删除元素很快

    1.2.2 缺点:

  7. 存取速度很慢。

    2 专业术语:

    2.1 头节点:

    头结点的数据类型和首节点的类型一样没有存放有效数据,最最前面的,是在首节点之前的,主要是为了方便对链表的操作。
    头结点有可能很大,占的内存可能大,假设我想造一个函数输出所有链表的值,那你如果不用头指针类型做形参,那由于不同链表的头节点不一样大小,这样就没办法找出形参

    2.2 首节点:第一个有效节点

    2.3 尾节点:最后一个有效节点

    2.4 头指针:(指向头)指向头节点的指针变量

  • 存放头结点的地址

    2.5 尾指针:指向尾节点的指针

  • 存放尾节点的地址

    2.6确定一个链表需要几个参数:

    (或者说如果期望一个函数对链表进行操作我们至少需要接收链表的那些信息???)

  • 只需要一个参数:头指针,因为通过它我们可以推出链表的所有信息。

(链表的程序最好一定要自己敲出来)

2.7 创建一个Node

  1. /*
  2. * @Descripttion:
  3. * @version: Beta-v1.0.0
  4. * @Author: jhong.tao
  5. * @Date: 2020-11-29 19:37:19
  6. * @LastEditTime: 2020-11-29 19:41:38
  7. */
  8. # include <stdio.h>
  9. typedef struct Node{
  10. int data; // 数据域
  11. struct Node * pNext; // 指针域
  12. }NODE,* PNODE;
  13. int main(void){
  14. return 0;
  15. }

3. 链表的分类

  • 单链表
  • 双链表: 每一个节点有两个指针域
  • 循环链表:能通过任何一个节点找到其他所有的节点
  • 非循环链表

java中变成垃圾内存则会自动释放,但是C和C++则不会,所以要手动释放,否则会引起内存泄露。delete等于free

4 链表相关的操作(算法)

  • 算法:狭义的算法是与数据的存储方式密切相关

    1. 广义的算法是与数据的存储方式无关
  • 泛型:(给你一种假象,只不过牛人从内部都弄好了)

    1. 利用某种技术达到的效果就是:不同的存储方式,执行的操作是一样的
  • 算法的真正学法:很多算法你根本解决不了!!!!!!

因为很多都属于数学上的东西,所以我们把答案找出来,如果能看懂就行,但是大部分人又看不懂,分三步,按照流程,语句试数。这个过程肯定会不断地出错,所以不断出错,不断改错,这样反复敲很多次,才能有个提高。实在看不懂 就先背会。

4.1 遍历

4.2 查找

4.3 清空

4.4 销毁

4.5 求长度

4.6 排序

4.7 删除节点

  1. r = p->pNext;
  2. p->pNext = p->pNetx->pNext;
  3. free(r);

4.8 插入节点

  1. q->pNext = p->pNext;
  2. p->pNext = q;

5 单链表操作code

  1. /*
  2. * @Descripttion:
  3. * @version: Beta-v1.0.0
  4. * @Author: jhong.tao
  5. * @Date: 2020-11-30 16:38:12
  6. * @LastEditTime: 2020-12-01 21:11:37
  7. */
  8. # include <stdio.h>
  9. # include <stdlib.h>
  10. # include <stdbool.h>
  11. /**
  12. * @name: struct Node
  13. * @brief: 链表的每一个节点,
  14. * @param {int data:链表节点中存放的数据}
  15. * @param {struct Node* next:链表节点指向下一个节点的指针}
  16. */
  17. typedef struct Node{
  18. int data; // 数据域
  19. struct Node* next; // 指针域
  20. }Node,*pNode;
  21. /**
  22. * @name: struct List
  23. * @brief: 定义的一个链表,包含了链表的头指针,尾指针和长度
  24. * @param {pNode pHeat:链表的头结点指针}
  25. * @param {pNode pTail:链表的尾指针}
  26. * @param {int cnt:链表的长度}
  27. */
  28. typedef struct List{
  29. pNode pHeat; // 头指针
  30. pNode pTail; // 尾指针
  31. int cnt; // 链表长度
  32. }List,*pList;
  33. // 函数声明
  34. pNode create_node(int val); // 创建一个节点
  35. pList create_list(int listName); // 创建链表
  36. void print_List(pList pl); // 遍历链表
  37. bool list_append(pList pl, int val); // 添加结点
  38. void free_list(pList pl); // 释放链表
  39. bool is_empty(pList pl); // 判断链表是否为空
  40. bool insert_list(pList pl, int pos, int val); // 插入数据
  41. pNode find_list(pList pl, int val); // 查找节点
  42. bool delet_list(pList pl, int val); // 删除一个节点
  43. void sort_list(pList pl); // 对链表进行升序排序
  44. int main(void){
  45. pList pl = create_list(0);
  46. list_append(pl,1);
  47. list_append(pl,2);
  48. insert_list(pl,1,4);
  49. print_List(pl);
  50. sort_list(pl);
  51. print_List(pl);
  52. printf("查找到2的值为:%d\n",find_list(pl,2)->next->data);
  53. free_list(pl);
  54. return 0;
  55. }
  56. /**
  57. * @name: void sort_list(pList pl)
  58. * @brief: 对链表进行升序排序
  59. * @param {pList pl:链表地址}
  60. * @return {无}
  61. */
  62. void sort_list(pList pl){
  63. /* 方法一:取出链表的每个节点的数据存放在数组中,对数组排序,然后将数据从新写回链表
  64. 方法二:直接对链表进行选择排序,只操作数据不操作节点,本算法
  65. */
  66. if(pl->pHeat->next == NULL) // 如果链表为空则终止
  67. return;
  68. pNode pi = pl->pHeat->next; // 已排序游标指针,永远指向已排序的最后也是最大的那个节点
  69. pNode pj = pi->next; // 选择排序指针
  70. pNode pMin = pi; // 选择指针,每次都去寻找一个最小的节点
  71. int intTemp;
  72. while(pi!=NULL){
  73. // 寻找未排序的最小节点
  74. while(pj!=NULL){
  75. if(pj->data < pMin->data)
  76. pMin = pj;
  77. pj = pj->next;
  78. }
  79. // 如果在未排序的队列中第一个节点就是最小节点的情况,直接后移
  80. if(pMin == pi){
  81. pi = pi->next;
  82. pMin = pi;
  83. if(pj !=NULL) // 当循环到最后一个节点时会出现pi->next=null的情况
  84. pj = pi->next;
  85. }else{
  86. // 如果未排序队列的第一个节点不是最小的,需要交换数据
  87. intTemp = pi->data;
  88. pi->data = pMin->data;
  89. pMin->data = intTemp;
  90. pi = pi->next;
  91. pMin = pi;
  92. pj = pi->next;
  93. }
  94. }
  95. }
  96. /**
  97. * @name: bool delet_list(pList pl, int val)
  98. * @brief: 删除一个第一次出现值为val的节点
  99. * @param {pList pl:链表地址}
  100. * @param {int val:要删除数}
  101. * @return {true:表示删除成功}
  102. */
  103. bool delet_list(pList pl, int val){
  104. pNode p = find_list(pl, val); // 获取当前节点的前驱的地址
  105. if(p==NULL)
  106. return false;
  107. pNode pTemp = p->next; // 获取当前节点的地址
  108. p->next = pTemp->next;
  109. free(pTemp);
  110. }
  111. /**
  112. * @name: bool find_list(pList pl, int val)
  113. * @brief: 查找首次在链表中出现的节点的前一个节点的地址
  114. * @param {pList pl:链表地址}
  115. * @param {int val:备查找的数}
  116. * @return {找到返回该节点前一个节点的地址,找不到返回NULL}
  117. */
  118. pNode find_list(pList pl, int val){
  119. pNode p = pl->pHeat; // 游标指针指向头节点
  120. while(p != NULL){
  121. if(val == p->next->data)
  122. return p;
  123. p = p->next;
  124. }
  125. return NULL;
  126. }
  127. /**
  128. * @name: insert_list(pList pl, int pos, int val)
  129. * @brief: 向链表中插入一个数据
  130. * @param {pList pl:链表地址}
  131. * @param {int pos:插入链表的位置,计数从1开始}
  132. * @param {int val:需要插入的数值}
  133. * @return {true:表示插入成功}
  134. */
  135. bool insert_list(pList pl, int pos, int val){
  136. // 位置小于1越界异常,插入失败
  137. if(pos<1){
  138. printf("越界异常,插入失败\n");
  139. return false;
  140. }
  141. // 位置比当前链表长则直接追加
  142. if(pos>pl->cnt){
  143. if(list_append(pl,val))
  144. return true;
  145. return false;
  146. }
  147. // 位置落在 1到链表长度之间
  148. pNode pn = create_node(val);
  149. if(pn == NULL)
  150. return false;
  151. pNode p = pl->pHeat; // 游标指针指向 头节点
  152. for(int i=0; i<pos; i++){
  153. if(i+1 == pos){
  154. pn->next = p->next;
  155. p->next = pn;
  156. pl->cnt++;
  157. return true;
  158. }
  159. p = p->next;
  160. }
  161. }
  162. /**
  163. * @name: bool is_empty(pList pl)
  164. * @brief: 判断链表是否为空
  165. * @param {pList pl:链表地址}
  166. * @return {true表示链表为空}
  167. */
  168. bool is_empty(pList pl){
  169. if(pl->pHeat->next == NULL)
  170. return true;
  171. return false;
  172. }
  173. /**
  174. * @name: void free_list(pList pl)
  175. * @brief: 释放一个链表
  176. * @param {pList pl:链表的头指针}
  177. * @return {无}
  178. */
  179. void free_list(pList pl){
  180. if (pl==NULL)
  181. return;
  182. pNode p = pl->pHeat->next; // 遍历链表的指针,获取链表的首节点
  183. while(p != NULL){
  184. pl->pHeat->next = p->next;
  185. free(p); // 循环释放每个节点
  186. p = pl->pHeat->next; // 更新p,再次指向首节点
  187. }
  188. free(pl->pHeat); // 释放链表头结点
  189. free(pl); // 释放链表
  190. }
  191. /**
  192. * @name: pList create_list()
  193. * @brief: 创建一个链表,包含头,尾指针和链表长度
  194. * @param {int listName:链表的头结点存放的数据,可以表示链表的编号或者名称}
  195. * @return {pList pl:链表的地址}
  196. */
  197. pList create_list(int listName){
  198. // 创建头结点
  199. pList pl = (pList)malloc(sizeof(List));
  200. if(NULL == pl){
  201. printf("链表创建失败");
  202. exit(-1);
  203. }
  204. pNode pHead = (pNode)malloc(sizeof(Node));
  205. if(NULL == pHead){
  206. printf("链表创建失败");
  207. exit(-1);
  208. }
  209. pHead->data = listName; // 头结点,记录链表名称
  210. pHead->next = NULL; // 头结点,指针域置空
  211. pl->pHeat = pHead; // 头指针,指向头结点
  212. pl->cnt = 0; // 初始化链表,长度,头结点不算入链表长度
  213. pl->pTail = pHead; // 尾结点,初始化链表时也指向头结点
  214. return pl;
  215. }
  216. /**
  217. * @name: pNode create_node(int val)
  218. * @brief: 创建链表的每一个节点
  219. * @param {int val:链表节点中保存的数据}
  220. * @return {pNode pHead:生成的链表节点的地址}
  221. */
  222. pNode create_node(int val){
  223. pNode pHead = (pNode)malloc(sizeof(Node)); // 创建一个链表节点
  224. if(NULL != pHead){
  225. pHead->data = val; // 给节点数据域赋值
  226. pHead->next = NULL; // 置空节点指针域
  227. }
  228. return pHead;
  229. }
  230. /**
  231. * @name: bool list_append(pList pl, int val)
  232. * @brief: 向链表中添加数据
  233. * @param {pList pl:链表地址}
  234. * @param {int val:添加的值}
  235. * @return {true:表示添加成功}
  236. */
  237. bool list_append(pList pl, int val){
  238. pNode pn = create_node(val); // 创建一个结点
  239. if(pn == NULL)
  240. return false;
  241. pl->pTail->next = pn; // 将新节点挂在链表尾部
  242. pl->pTail = pn; // 更新链表的尾指针
  243. pl->cnt++; // 更新链表长度
  244. return true;
  245. }
  246. /**
  247. * @name: void print_List(pList pl)
  248. * @brief: 打印链表
  249. * @param {pList pl:链表地址}
  250. * @return {无返回值}
  251. */
  252. void print_List(pList pl){
  253. pNode p = pl->pHeat->next; // 获取链表首节点,即链表第一个存放真实数据的节点,不是头结点
  254. printf("%d 链表的长度为:%d\n", pl->pHeat->data, pl->cnt);
  255. while(p != NULL){
  256. printf("%d ",p->data); // 打印链表的每个节点的数据
  257. p = p->next;
  258. }
  259. printf("\n");
  260. }