• [C语言]排序的大乱炖——喵喵的成长记


    宝子,你不点个赞吗?不评个论吗?不收个藏吗?

    最后的最后,关注我,关注我,关注我,你会看到更多有趣的博客哦!!!

    喵喵喵,你对我真的很重要。


    前言

    小喵喵课堂开课来,今天我们学习七个常见的排序,哦哦耶!!!

    喵喵今天也要加油哦!

    来吧,不乱叫,上导图:

    目录

    前言

    八大经典排序的概述

    直接插入排序

    希尔排序

    选择排序

    堆排序

    冒泡排序

    快速排序(快排)

    归并排序

    总结


    ┗|`O′|┛ 嗷~~,怎么能忘了基数排序呢?补上补上:

    八大经典排序的概述

    八大排序算法是指常见的八种经典排序算法,它们分别是冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序和基数排序。

    1. 冒泡排序:比较相邻的元素,如果顺序错误就交换它们,重复这个过程直到整个数组有序。
    2. 选择排序:每次从待排序的数组中选择最小(或最大)的元素放在已排序数组的末尾,直到整个数组有序。
    3. 插入排序:遍历数组,将当前元素插入已排好序的子数组中的适当位置,直到整个数组有序。
    4. 希尔排序:通过设置间隔将数组分组,对每个分组进行插入排序,逐渐减小间隔,直到间隔为1时进行最后一次插入排序。
    5. 归并排序:采用分治的思想,将待排序数组划分为较小的子数组,然后递归地对子数组进行排序,并将排好序的子数组合并成更大的有序数组。
    6. 快速排序:选取一个基准元素,将数组划分为左右两部分,左边部分都比基准元素小,右边部分都比基准元素大,在递归地对左右两部分进行快速排序。
    7. 堆排序:将待排序数组构建成一个大顶堆,然后将堆顶元素与最后一个元素交换并重新调整堆,重复这个过程直到整个数组有序。
    8. 基数排序:根据元素的每个位上的值进行排序,先按低位排序,再按高位排序,直到所有位都排序完成。

    直接插入排序

    直接插入的排序规则,如图所示:

    将end和tmp代入进去思考,一来的3,就算起始点,排好了位置,作为end,然后tmp是44,44>3,tmp>end,所以不交换位置,算排好了,end+1,tmp+1。那么第二次比较,end是44,tmp是38,end>tmp,交换位置,38排在44前面。那么第三次比较,5比44小,比38小,排在38前面。以此类推,循环排好数组。

    1. void InSert(int* a, int n)
    2. {
    3. for (int i = 1; i < n; i++)
    4. {
    5. int end = i - 1;
    6. int tmp = a[i];
    7. while (end >= 0)
    8. {
    9. if (tmp < a[end])
    10. {
    11. a[end + 1] = a[end];
    12. end--;
    13. }
    14. else
    15. {
    16. break;
    17. }
    18. }
    19. a[end + 1] = tmp;
    20. }
    21. }

    希尔排序

    希尔排序是直接插入排序的升级版,先对数组进行有规律的分组,分成多个组,然后每个小组进行直接插入排序。每个小组排完后,再进行一次直接插入排序,就要轻松地多,能够快速排好,大大提高了排序的效率。

    分成绿,蓝,红三组,分别进行直接插入排序。

    1. void InsertSort(int* a, int n)
    2. {
    3. int gap = 3;
    4. for (j=0;j<gap;j++)
    5. {
    6. for (int i = j; i < n-gap; i+=gap)
    7. {
    8. int end = i;
    9. int tmp = a[i+gap];
    10. while (end >= 0)
    11. {
    12. if (tmp < a[end])
    13. {
    14. a[end + gap] = a[end];
    15. end-=gap;
    16. }
    17. else
    18. {
    19. break;
    20. }
    21. }
    22. a[end + gap] = tmp;
    23. }
    24. }
    25. }
    1. void InsertSort(int* a, int n)
    2. {
    3. int gap = n;
    4. int j = 0;
    5. while (gap > 1)
    6. {
    7. gap = gap / 2;
    8. for (j = 0; j < gap; j++)
    9. {
    10. for (int i = j; i < n - gap; i += gap)
    11. {
    12. int end = i;
    13. int tmp = a[i + gap];
    14. while (end >= 0)
    15. {
    16. if (tmp < a[end])
    17. {
    18. a[end + gap] = a[end];
    19. end -= gap;
    20. }
    21. else
    22. {
    23. break;
    24. }
    25. }
    26. a[end + gap] = tmp;
    27. }
    28. }
    29. }
    30. }


    选择排序

    选择排序,很直观就是要选择,我们先选择3作为有序数组,然后从3后面的数组中选出最小的数,并于3进行比较,谁小谁站在前面。2比3小,2与3交换位置。依次往后推,拍成有序数组。

    喵,很难评,这是简单版的一个点移动,而我们一般上的是困难版的两个点,喵,不好理解,喵喵,也是搞了好久,才明白滴,喵。那么让我们开始吧!猫猫队,冲冲冲!

    两个点的移动(建议结合代码,自己也跟着走一走,感觉不就来了嘛!):

    1. //这是两个点的移动
    2. void SelectSort(int* a, int n)
    3. {
    4. int left = 0, right = n - 1;
    5. while (left < right)
    6. {
    7. int mini = left, maxi = left;
    8. for (int i = left + 1; i <= right; i++)
    9. {
    10. if (a[i] < a[mini])
    11. {
    12. mini = i;
    13. }
    14. if (a[i] > a[maxi])
    15. {
    16. maxi = i;
    17. }
    18. }
    19. Swap(&a[left], &a[mini]);
    20. // 如果left和maxi重叠,交换后修正一下
    21. if (left == maxi)
    22. {
    23. maxi = mini;
    24. }
    25. Swap(&a[right], &a[maxi]);
    26. ++left;
    27. --right;
    28. }
    29. }

    堆排序

    就是以层序的方式从下往上,从大到小的排。升序建大堆,降序建小堆。

    1. // 左右子树都是大堆/小堆
    2. void AdjustDown(int* a, int n, int parent)
    3. {
    4. int child = parent * 2 + 1;
    5. while (child < n)
    6. {
    7. // 选出左右孩子中大的那一个
    8. if (child + 1 < n && a[child + 1] > a[child])
    9. {
    10. ++child;
    11. }
    12. if (a[child] > a[parent])
    13. {
    14. Swap(&a[child], &a[parent]);
    15. parent = child;
    16. child = parent * 2 + 1;
    17. }
    18. else
    19. {
    20. break;
    21. }
    22. }
    23. }
    24. void HeapSort(int* a, int n)
    25. {
    26. // 建堆 -- 向下调整建堆 -- O(N)
    27. for (int i = (n - 1 - 1) / 2; i >= 0; --i)
    28. {
    29. AdjustDown(a, n, i);
    30. }
    31. // 自己先实现 -- O(N*logN)
    32. int end = n - 1;
    33. while (end > 0)
    34. {
    35. Swap(&a[end], &a[0]);
    36. AdjustDown(a, end, 0);
    37. --end;
    38. }
    39. }

    冒泡排序

    冒泡排序,好理解,教学意义重大。简单的来说就是从一端开始,两两交换,把小的换在前面去就OK了!

    1. void BubbleSort(int* a, int n)
    2. {
    3. for (int j = 0; j < n; j++)
    4. {
    5. bool exchange = false;
    6. for (int i = 1; i < n - j; i++)
    7. {
    8. if (a[i - 1] > a[i])
    9. {
    10. Swap(&a[i - 1], &a[i]);
    11. exchange = true;
    12. }
    13. }
    14. if (exchange == false)
    15. {
    16. break;
    17. }
    18. }
    19. }

    快速排序(快排)

    快排有很多种方法,比如hoare法,挖坑法,前后指针法:

    1.hoare法:

    无言,如图所示

    1. int GetMidNumi(int* a, int left, int right)
    2. {
    3. int mid = (left + right) / 2;
    4. if (a[left] < a[mid])
    5. {
    6. if (a[mid] < a[right])
    7. {
    8. return mid;
    9. }
    10. else if (a[left] > a[right])
    11. {
    12. return left;
    13. }
    14. else
    15. {
    16. return right;
    17. }
    18. }
    19. else // a[left] > a[mid]
    20. {
    21. if (a[mid] > a[right])
    22. {
    23. return mid;
    24. }
    25. else if (a[left] < a[right])
    26. {
    27. return left;
    28. }
    29. else
    30. {
    31. return right;
    32. }
    33. }
    34. }
    35. int PartSort1(int* a, int left, int right)
    36. {
    37. // 三数取中
    38. int midi = GetMidNumi(a, left, right);
    39. if (midi != left)
    40. Swap(&a[midi], &a[left]);
    41. int keyi = left;
    42. while (left < right)
    43. {
    44. // 右边找小
    45. while (left < right && a[right] >= a[keyi])
    46. --right;
    47. // 左边找大
    48. while (left < right && a[left] <= a[keyi])
    49. ++left;
    50. Swap(&a[left], &a[right]);
    51. }
    52. Swap(&a[keyi], &a[left]);
    53. keyi = left;
    54. return keyi;
    55. }

    挖坑法:

    1. int GetMidNumi(int* a, int left, int right)
    2. {
    3. int mid = (left + right) / 2;
    4. if (a[left] < a[mid])
    5. {
    6. if (a[mid] < a[right])
    7. {
    8. return mid;
    9. }
    10. else if (a[left] > a[right])
    11. {
    12. return left;
    13. }
    14. else
    15. {
    16. return right;
    17. }
    18. }
    19. else // a[left] > a[mid]
    20. {
    21. if (a[mid] > a[right])
    22. {
    23. return mid;
    24. }
    25. else if (a[left] < a[right])
    26. {
    27. return left;
    28. }
    29. else
    30. {
    31. return right;
    32. }
    33. }
    34. }
    35. int PartSort2(int* a, int left, int right)
    36. {
    37. // 三数取中
    38. int midi = GetMidNumi(a, left, right);
    39. if (midi != left)
    40. Swap(&a[midi], &a[left]);
    41. // 21:10继续
    42. int key = a[left];
    43. int hole = left;
    44. while (left < right)
    45. {
    46. // 右边找小
    47. while (left < right && a[right] >= key)
    48. --right;
    49. a[hole] = a[right];
    50. hole = right;
    51. // 左边找大
    52. while (left < right && a[left] <= key)
    53. ++left;
    54. a[hole] = a[left];
    55. hole = left;
    56. }
    57. a[hole] = key;
    58. return hole;
    59. }

    前后指针法:

    1. int GetMidNumi(int* a, int left, int right)
    2. {
    3. int mid = (left + right) / 2;
    4. if (a[left] < a[mid])
    5. {
    6. if (a[mid] < a[right])
    7. {
    8. return mid;
    9. }
    10. else if (a[left] > a[right])
    11. {
    12. return left;
    13. }
    14. else
    15. {
    16. return right;
    17. }
    18. }
    19. else // a[left] > a[mid]
    20. {
    21. if (a[mid] > a[right])
    22. {
    23. return mid;
    24. }
    25. else if (a[left] < a[right])
    26. {
    27. return left;
    28. }
    29. else
    30. {
    31. return right;
    32. }
    33. }
    34. }
    35. int PartSort3(int* a, int left, int right)
    36. {
    37. // 三数取中
    38. int midi = GetMidNumi(a, left, right);
    39. if (midi != left)
    40. Swap(&a[midi], &a[left]);
    41. int keyi = left;
    42. int prev = left;
    43. int cur = left + 1;
    44. while (cur <= right)
    45. {
    46. if (a[cur] < a[keyi] && ++prev != cur)
    47. Swap(&a[cur], &a[prev]);
    48. ++cur;
    49. }
    50. Swap(&a[prev], &a[keyi]);
    51. keyi = prev;
    52. return keyi;
    53. }

    归并排序(递归):

    1. int GetMidNumi(int* a, int left, int right)
    2. {
    3. int mid = (left + right) / 2;
    4. if (a[left] < a[mid])
    5. {
    6. if (a[mid] < a[right])
    7. {
    8. return mid;
    9. }
    10. else if (a[left] > a[right])
    11. {
    12. return left;
    13. }
    14. else
    15. {
    16. return right;
    17. }
    18. }
    19. else // a[left] > a[mid]
    20. {
    21. if (a[mid] > a[right])
    22. {
    23. return mid;
    24. }
    25. else if (a[left] < a[right])
    26. {
    27. return left;
    28. }
    29. else
    30. {
    31. return right;
    32. }
    33. }
    34. }
    35. int PartSort3(int* a, int left, int right)
    36. {
    37. // 三数取中
    38. int midi = GetMidNumi(a, left, right);
    39. if (midi != left)
    40. Swap(&a[midi], &a[left]);
    41. int keyi = left;
    42. int prev = left;
    43. int cur = left + 1;
    44. while (cur <= right)
    45. {
    46. if (a[cur] < a[keyi] && ++prev != cur)
    47. Swap(&a[cur], &a[prev]);
    48. ++cur;
    49. }
    50. Swap(&a[prev], &a[keyi]);
    51. keyi = prev;
    52. return keyi;
    53. }
    54. void QuickSort(int* a, int left, int right)
    55. {
    56. if (left >= right)
    57. return;
    58. int keyi = PartSort3(a, left, right);
    59. QuickSort(a, left, keyi - 1);
    60. QuickSort(a, keyi + 1, right);
    61. }

    总结

    疯了,疯了,怎么才能讲清楚啊,白话文一大堆,还是图来说清楚,没看清楚的啾咪,留言喵喵鸭,你的明白,就是我的成长,酸Q喵。


    宝子,你不点个赞吗?不评个论吗?不收个藏吗?

    最后的最后,关注我,关注我,关注我,你会看到更多有趣的博客哦!!!

    喵喵喵,你对我真的很重要。

  • 相关阅读:
    VUE build:gulp打包:测试、正式环境
    请解释一下 CSS3 的 Flexbox(弹性盒布局模型), 以及适用场景?
    重新认识 IP地址
    java学习--day23(线程池)
    一文讲解ARMv8内存属性与类型(Memory types and attributes)简介
    qt pro如何增加自定义值为的字符串的宏
    【python爬虫】—星巴克产品
    技能大赛物联网赛项参赛软件建设方案
    使用uwsgi部署Flask
    电脑怎么迁移游戏资源,数据迁移能把游戏数据迁移吗
  • 原文地址:https://blog.csdn.net/ormstq/article/details/133652053