深圳幻海软件技术有限公司 欢迎您!

链表的排序

2023-03-31

最近在写一个学生管理系统,还没有写完就已经遇到了很多困难,也学到了很多,本文谨记录本人对链表排序的一些理解。冒泡排序与直接排序就冒泡排序与直接排序而言,链表与数相似,先比较两个变量的大小,再交换两个变量的内容。交换的仅是变量所存的内容,链表(数组)的每个节点(元素)的位置关系不变。 交换n

最近在写一个学生管理系统,还没有写完就已经遇到了很多困难,也学到了很多,本文谨记录本人对链表排序的一些理解。

冒泡排序与直接排序

  • 就冒泡排序与直接排序而言,链表与数相似,先比较两个变量的大小,再交换两个变量的内容。
  • 交换的仅是变量所存的内容,链表(数组)的每个节点(元素)的位置关系不变。

 交换number1和number2后:

 改变的只是变量a和b的内容(number),a和b的位置关系没有改变。

  •  比大小与数组完全一致,只需取出节点内的number进行比较,难点在于交换number。
  • 如果一个节点中只储存一个number,可以直接将number取出进行交换。
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define N 5
  4. typedef struct node{
  5. int number;
  6. struct node* next;
  7. }NODE;
  8. int main()
  9. {
  10. int i;
  11. NODE* head = (NODE*)malloc(sizeof(NODE));
  12. head->next = NULL;
  13. NODE* last = head;
  14. //尾插法创建含有5个节点的链表
  15. for (i = 0; i < N; i++)
  16. {
  17. NODE* p = (NODE*)malloc(sizeof(NODE));
  18. scanf("%d", &p->number);
  19. last->next = p;
  20. last = p;
  21. p->next = NULL;
  22. }
  23. //直接排序法从大到小进行排序
  24. for (NODE* p = head->next; p->next; p = p->next)
  25. for (NODE* q = p->next; q; q = q->next)
  26. if (q->number > p->number)
  27. {
  28. int t = p->number;
  29. p->number = q->number;
  30. q->number = t;
  31. }
  32. //输出链表
  33. for (NODE* p = head->next; p != 0; p = p->next)
  34. printf("%d\n", p->number);
  35. }
  • 当一个节点内有很多内容时,如学生信息管理系统,一个节点包含姓名、成绩、学号等多个内容,如果把节点内的全部内容取出,然后交换,将十分麻烦。

为什么我们不直接通过结构体的赋值来进行节点的交换?因为结构体赋值会把节点的next也进行交换,会打乱节点相对位置,破坏掉整条链表。所以我们在结构体赋值之前,提前记录下所交换节点的next,既他们的位置关系,交换结束后,将他们的位置关系还原。

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define N 5
  4. typedef struct node{
  5. int number;
  6. struct node* next;
  7. }NODE;
  8. int main()
  9. {
  10. int i;
  11. NODE* head = (NODE*)malloc(sizeof(NODE));
  12. head->next = NULL;
  13. NODE* last = head;
  14. //尾插法创建含有5个节点的链表
  15. for (i = 0; i < N; i++)
  16. {
  17. NODE* p = (NODE*)malloc(sizeof(NODE));
  18. scanf("%d", &p->number);
  19. last->next = p;
  20. last = p;
  21. p->next = NULL;
  22. }
  23. //直接排序法从大到小进行排序
  24. for (NODE* p = head->next; p->next; p = p->next)
  25. for (NODE* q = p->next; q; q = q->next)
  26. if (q->number > p->number)
  27. {
  28. //提前记录p和q的next
  29. NODE* p_next = p->next;
  30. NODE* q_next = q->next;
  31. //整体交换p和q
  32. NODE t = *p;
  33. *p = *q;
  34. *q = t;
  35. //将p和q的位置关系还原
  36. p->next = p_next;
  37. q->next = q_next;
  38. }
  39. //输出链表
  40. for (NODE* p = head->next; p != 0; p = p->next)
  41. printf("%d\n", p->number);
  42. }

插入排序

  • 链表和数组的插入排序差异较大,本质在于两者插入新节点(元素)的方式不同,数组需要整体后移,而链表只需改变节点的next。
  • 数组仍不改变每个元素间的相对位置关系,只改变元素所包含的内容,而链表是整体搬家,改变的是节点间的位置关系。

重新排序后: 

不要在意我按照什么规则进行的排序,主要突出一个乱,重点在于理解他们的位置关系发生了变化。

  • 插入排序的思路主要是,将链表分为两条,一条主链表等待被插入,一条从链表逐个插入到主链表中。
  • 具体操作需要对两条链表都进行遍历,如果从链表中的某个节点没有找到位置可以插入,需要补在主链表的末尾。
  • 所以我们需要两次嵌套的for循环进行遍历,需要一个flag记录节点是否成功插入。
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define N 5
  4. typedef struct node{
  5. int number;
  6. struct node* next;
  7. }NODE;
  8. int main()
  9. {
  10. int i;
  11. NODE* head = (NODE*)malloc(sizeof(NODE));
  12. head->next = NULL;
  13. NODE* last = head;
  14. //尾插法创建含有5个节点的链表
  15. for (i = 1; i <= N; i++)
  16. {
  17. NODE* p = (NODE*)malloc(sizeof(NODE));
  18. scanf("%d", &p->number);
  19. last->next = p;
  20. last = p;
  21. p->next = NULL;
  22. }
  23. //插入排序法从大到小进行排序
  24. //将原链表拆分成两个链表
  25. NODE* p = head->next;
  26. NODE* q = p->next;
  27. p->next = NULL;
  28. int flag = 0;
  29. //将链表q逐一插入链表p中
  30. for (NODE* r = q->next; q; q = r)
  31. {
  32. //flag为0说明q的节点还未插入链表p中
  33. flag = 0;
  34. //记录q的下一个节点
  35. r = q->next;
  36. for (NODE* last = head; p; last = p, p = p->next)
  37. if (q->number > p->number)
  38. {
  39. //找到插入位置flag变为1
  40. flag = 1;
  41. q->next = p;
  42. last->next = q;
  43. break;
  44. }
  45. //我这里的起点有两个,p和last
  46. //for循环可以帮我们复原last
  47. //我们还需手动将p的位置复原
  48. p = head->next;
  49. //flag为0即该节点应补在链表p的末尾
  50. if (flag == 0)
  51. {
  52. NODE* last = head;
  53. //找到链表p的最后一个节点
  54. while (last->next)
  55. last = last->next;
  56. last->next = q;
  57. q->next = NULL;
  58. }
  59. }
  60. //输出链表
  61. for (NODE* p = head->next; p != 0; p = p->next)
  62. printf("%d\n", p->number);
  63. }

这个插入排序的代码不管是可读性还是执行效率都有可优化的空间,仅作为学习记录,随着本人的后续学习,可能会有完善。

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览42577 人正在系统学习中