双向循环链表的选择排序

比眉伴天荒 2022-06-04 01:39 288阅读 0赞

一、复习数组的选择排序

选择排序属于蛮力法。
首先,扫描整个列表,找到最小的元素,将其和第一个元素交换位置;然后从第二个元素开始扫描列表,找到最小的元素,再将其和第二个元素交换位置……直到从倒数第二个元素开始扫描列表,找到最小的元素,将其和倒数第二个元素交换位置。

假设有4个数,选择排序的示意图如下。

这里写图片描述

二、以Linux内核链表为例,进行选择排序

1. 完整代码

  1. #include <stdio.h>
  2. #include "list.h"
  3. struct data_info {
  4. int data;
  5. struct list_head list;
  6. };
  7. int cmp_data(struct list_head *a, struct list_head *b)
  8. {
  9. struct data_info *pa = list_entry(a, struct data_info, list);
  10. struct data_info *pb = list_entry(b, struct data_info, list);
  11. return pa->data - pb->data;
  12. }
  13. void swap(struct list_head *a, struct list_head *b)
  14. {
  15. struct list_head flag = {NULL, NULL};
  16. __list_add(&flag, b->prev, b);
  17. list_del(b);
  18. __list_add(b, a->prev, a);
  19. list_del(a);
  20. __list_add(a, flag.prev, &flag);
  21. list_del(&flag);
  22. }
  23. void select_sort(struct list_head *head,
  24. int(*cmp)(struct list_head *a,
  25. struct list_head *b))
  26. {
  27. struct list_head *i, *j, *min;
  28. list_for_each(i, head) {
  29. if(i == head->prev)
  30. break; //当i指向最后一个结点时,排序完成
  31. min = i; //用min指向最小的结点
  32. j = i->next;
  33. list_for_each_from(j, head) {
  34. if (cmp(j, min) < 0) {
  35. min = j;
  36. }
  37. }
  38. if (min != i) {
  39. swap(min, i);
  40. i = min; //i指针归位
  41. }
  42. }
  43. }
  44. int main(void)
  45. {
  46. struct data_info s[] = {
  47. {
  48. 6}, {
  49. 4}, {
  50. 7}, {
  51. 9}, {
  52. 2}, {
  53. 8}, {
  54. 5}, {
  55. 1}, {
  56. 3}, {
  57. 7}};
  58. LIST_HEAD(head);
  59. int i;
  60. for (i = 0; i < sizeof s/ sizeof *s; ++i)
  61. {
  62. list_add_tail(&s[i].list, &head);
  63. } //尾插,构成链表
  64. struct data_info *pdata = NULL;
  65. list_for_each_entry(pdata, &head, list)
  66. {
  67. printf("%d ", pdata->data);
  68. }
  69. printf("\n"); //排序之前
  70. select_sort(&head, cmp_data); //进行排序
  71. list_for_each_entry(pdata, &head, list)
  72. {
  73. printf("%d ", pdata->data);
  74. }
  75. printf("\n"); //排序之后
  76. return 0;
  77. }

上面代码的实验结果是:

6 4 7 9 2 8 5 1 3 7
1 2 3 4 5 6 7 7 8 9

2. 代码解析

2.1 swap函数

可以参考我的博文http://blog.csdn.net/longintchar/article/details/78638975

2.2 list_for_each_from

  1. #define list_for_each_from(cur, head) \
  2. for (; cur != head; cur = (cur)->next)

也就是说,不进行cur的初始化(cur需要在进入for循环之前被初始化)。

2.3 select_sort函数

  1. void select_sort(struct list_head *head,
  2. int(*cmp)(struct list_head *a,
  3. struct list_head *b))
  4. {
  5. struct list_head *i, *j, *min;
  6. list_for_each(i, head) {
  7. if(i == head->prev)
  8. break; //当i指向最后一个结点时,排序完成
  9. min = i; //用min指向最小的结点
  10. j = i->next;
  11. list_for_each_from(j, head) {
  12. if (cmp(j, min) < 0) {
  13. min = j;
  14. }
  15. }
  16. if (min != i) {
  17. swap(min, i);
  18. i = min; //i指针归位
  19. }
  20. }
  21. }

7~9行:i的取值从第一个结点的地址到倒数第二个结点的地址;
第10行:假设i指向最小的结点,用min保存最小的结点的地址;
11~14行:ji的下一个结点开始遍历,一直到最后一个结点,这些结点加上i指向的结点,选出其中最小的,其地址由min保存;
17~18行:mini交换位置,此时,i指向的结点已经在最终位置上;
第19行:由于交换使i改变了位置,所以需要对其归位。为什么要这样做,可以参考我的上一篇博文(链接已经在上文给出)中的4.4节。

【完】

发表评论

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

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

相关阅读

    相关 循环双向

    一、循环链表 循环链表是与单链表一样,是一种链式的存储结构,所不同的是,循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。

    相关 双向循环

    一、双向链表 每个结点有两个指针域和若干数据域,其中一个指针域指向它的前趋结点,一个指向它的后继结点。它的优点是访问、插入、删除更方便,速度也快了。但“是以空间换时间”。

    相关 双向循环插入排序

    前两篇博文,我讨论了链表的冒泡排序和选择排序(以Linux内核链表为例),这篇文章,我想说说插入排序。 一、复习数组的插入排序 插入排序在算法思想中属于“减治法”。

    相关 双向循环选择排序

    一、复习数组的选择排序 选择排序属于蛮力法。 首先,扫描整个列表,找到最小的元素,将其和第一个元素交换位置;然后从第二个元素开始扫描列表,找到最小的元素,再将其和第二

    相关 双向循环

    双向链表 单链表的一个优点是结构简单,但是它也有一个缺点,即在单链表中只能通过一个结点的引用访问其后续结点,而无法直接访问其前驱结点, 要在单链表中找到某个结点的前驱结点