当前位置:   article > 正文

选择排序,冒泡排序,插入排序,快速排序及其优化

选择排序,冒泡排序,插入排序,快速排序及其优化

目录

1 选择排序

1.1 原理

1.2 具体步骤 

1.3 代码实现

1.4 优化

2 冒泡排序

2.1 原理

2.2 具体步骤

2.3 代码实现

2.4 优化

3 插入排序

3.1 原理

3.2 具体步骤 

3.3 代码实现

3.4 优化

4. 快速排序 

4.1 原理

4.2 具体步骤

4.3 代码实现 

4.4 优化 


为了讲解方便,以下排完序后,统一为升序

1 选择排序

1.1 原理

核心思想是通过不断地选择未排序序列中的最小元素,然后将其放到已排序序列的末尾(或未排序列的起始位置)

 

1.2 具体步骤 

1. 初始状态:所有元素初始都为未排序状态

2 在未排序元素中,找到最小的那个元素的下标

3 与未排序的第一个元素(已排序的末尾元素)交换位置

4 循环 2 ~ 3,直到所有元素都变为已排了的元素

1.3 代码实现

代码实现的关键点:找下标,换位置,以及循环条件。同时也是容易出错的点。

  1. #include<stdio.h>
  2. void sort(int* p, int n)
  3. {
  4. for (int i = 0; i < n - 1; i++) // < n 也可以,只是无意义的重复,效率更低
  5. {
  6. int min = i; // 找出的最小值,最后要放的位置的下标
  7. int j = i;
  8. for (j = i + 1; j < n; j++) // 可以=i,只是=i,无意义。
  9. {
  10. if (p[min] > p[j])
  11. min = j;
  12. // for循环结束后,j会++
  13. if (n - 1 == j)
  14. break;
  15. }
  16. // 交换数据
  17. int temp = p[i];
  18. p[i] = p[min];
  19. p[min] = temp;
  20. }
  21. }
  22. int main()
  23. {
  24. int arr[] = { 9,6,8,-1,0,5,2,8 };
  25. int sz = sizeof(arr) / sizeof(arr[0]);
  26. sort(arr, sz); // 选择排序
  27. for (int i = 0; i < sz; i++)
  28. {
  29. printf("%d ", arr[i]);
  30. }
  31. return 0;
  32. }

1.4 优化

 原来一趟只找出最小值,现在一趟既找出最小值,也找出最大值,循环的次数就减半了。

  1. #include<stdio.h>
  2. void swap(int* a, int* b)
  3. {
  4. int temp = *a;
  5. *a = *b;
  6. *b = temp;
  7. }
  8. void sort(int* p, int n)
  9. {
  10. // 循环趟数减半,可以了就要停止,不然就会继续换,反而无序
  11. for (int i = 0; i <= n / 2 + 1; i++)
  12. {
  13. int min = i; // 找出的最小值的下标
  14. int max = n -i - 1; // 找出的最大值的下标
  15. int j = i;
  16. for (j = i; j < n - i; j++)
  17. {
  18. if (p[min] > p[j])
  19. min = j;
  20. if (p[max] < p[j])
  21. max = j;
  22. if (n - 1 - i == j)
  23. break;
  24. }
  25. // 交换数据
  26. // 当最小值与最大值恰好位置相反,换两次=没换
  27. if (!(p[min] == p[n - 1 - i] || p[i] == p[max]))
  28. {
  29. swap(&p[i], &p[min]);
  30. swap(&p[n - 1 - i], &p[max]);
  31. }
  32. else
  33. {
  34. swap(&p[i], &p[n - 1 - i]);
  35. }
  36. }
  37. }
  38. int main()
  39. {
  40. int arr[] = { 9,6,8,-1,0,5,2,8 };
  41. int sz = sizeof(arr) / sizeof(arr[0]);
  42. sort(arr, sz); // 选择排序优化
  43. for (int i = 0; i < sz; i++)
  44. {
  45. printf("%d ", arr[i]);
  46. }
  47. return 0;
  48. }

2 冒泡排序

2.1 原理

核心思想是通过重复交换相邻元素来实现排序。

类比选择排序,相当于从右往左开始排,每次在未排序中找出最大值,放在已排序的前一个位置。

 

2.2 具体步骤

1. 从左往右相邻元素比较,让大的数不断右移

2. 循环1,直至每个已排序的元素 = 所有元素的个数

2.3 代码实现

关键点:循环条件的控制,以及交换(大的靠右)

  1. #include<stdio.h>
  2. void sort(int arr[], size_t sz)
  3. {
  4. for (int i = 0; i < sz - 1; i++)
  5. {
  6. // 在这里j可以 < sz - 1;
  7. // 在本段代码中交换位置是有条件的
  8. // < sz - 1;进去了也不会执行
  9. // 选择排序从哪开始到哪结束就必须是那样
  10. // 不可能在再在排了序中挑最大最小值
  11. for (int j = 0; j < sz - 1 - i; j++)
  12. {
  13. if (arr[j] > arr[j + 1])
  14. {
  15. int temp = arr[j];
  16. arr[j] = arr[j + 1];
  17. arr[j + 1] = temp;
  18. }
  19. }
  20. }
  21. }
  22. int main()
  23. {
  24. int arr[] = { 9,6,8,-1,0,5,2,8 };
  25. size_t sz = sizeof(arr) / sizeof(arr[0]);
  26. sort(arr, sz); // 冒泡排序
  27. for (int i = 0; i < sz; i++)
  28. {
  29. printf("%d ", arr[i]);
  30. }
  31. return 0;
  32. }

2.4 优化

如果我们一开始拿到的数组就是有序的话,我们还是不得不执行那循环套循环,效率就很低。我们可以先假设已达到了有序状态,如果交换了,就通过修改flag的值来办,这样就可以提前跳出循环了。

  1. #include<stdio.h>
  2. void sort(int arr[], size_t sz)
  3. {
  4. for (int i = 0; i < sz - 1; i++)
  5. {
  6. // 假设已经到达了有序状态
  7. int flag = 1;
  8. for (int j = 0; j < sz - 1 - i; j++)
  9. {
  10. if (arr[j] > arr[j + 1])
  11. {
  12. int temp = arr[j];
  13. arr[j] = arr[j + 1];
  14. arr[j + 1] = temp;
  15. // 交换了,说明无序
  16. flag = 0;
  17. }
  18. }
  19. if (flag)
  20. {
  21. break;
  22. }
  23. }
  24. }
  25. int main()
  26. {
  27. int arr[] = { 9,6,8,-1,0,5,2,8 };
  28. size_t sz = sizeof(arr) / sizeof(arr[0]);
  29. sort(arr, sz); // 冒泡排序优化
  30. for (int i = 0; i < sz; i++)
  31. {
  32. printf("%d ", arr[i]);
  33. }
  34. return 0;
  35. }

3 插入排序

3.1 原理

核心思想是构建有序序列将未排序的元素逐个插入到已排序的部分。

左边是已排序的,右边是未排序的。未排序的从左到右第一个,放到已排序列中开始交换,大的就右移,移到不能再移。

 

3.2 具体步骤 

1. 初始化:左边第一个是已排序的,右边都是未排序的。

2 交换位置:未排序的第一个进入排序中,比较大小,大的右移,移到不能再移

3 循环 1 ~ 2,直到遍历数组中的所有元素

3.3 代码实现

关键点: 交换位置的意识,递推的意识

  1. #include<stdio.h>
  2. void sort(int arr[], size_t sz)
  3. {
  4. // 外层每一次循环都会让已排序的元素+1
  5. for (int i = 1; i < sz; i++)
  6. {
  7. int j = i;
  8. while (j >= 1 && arr[j] < arr[j - 1])
  9. {
  10. // 交换位置
  11. arr[j - 1] = arr[j] ^ arr[j - 1];
  12. arr[j] = arr[j] ^ arr[j - 1];
  13. arr[j - 1] = arr[j] ^ arr[j - 1];
  14. j--;
  15. }
  16. }
  17. }
  18. int main()
  19. {
  20. int arr[] = { 9,6,8,-1,0,5,2,8 };
  21. size_t sz = sizeof(arr) / sizeof(arr[0]);
  22. sort(arr, sz); // 插入排序
  23. for (int i = 0; i < sz; i++)
  24. {
  25. printf("%d ", arr[i]);
  26. }
  27. return 0;
  28. }

3.4 优化

所谓优化算法就更接近于通俗意义上的插入,找到该插入的的地方,再进行插入。即对插入的位置实行二分查找。

  1. #include<stdio.h>
  2. // 返回值是该数插入进去后的下标
  3. int find(int arr[], int sz)
  4. {
  5. // 1 2 4 5 7 8 9 6
  6. int left = 0;
  7. int right = sz - 1;
  8. while (left < right)
  9. {
  10. // 防止陷入死循环
  11. if (left == right)
  12. break;
  13. int mid = right + (left - right) / 2;
  14. if (arr[mid] > arr[sz])
  15. {
  16. right = mid - 1;
  17. }
  18. else if (arr[mid] < arr[sz])
  19. {
  20. left = mid + 1;
  21. }
  22. else
  23. {
  24. right = left;
  25. break;
  26. }
  27. }
  28. if (arr[sz] < arr[right])
  29. {
  30. return right;
  31. }
  32. else
  33. {
  34. return right + 1;
  35. }
  36. }
  37. void sort(int arr[], size_t sz)
  38. {
  39. for (int i = 1; i < sz; i++)
  40. {
  41. // 本质:二分查找 + 交换
  42. int temp = arr[i]; // 将要排序的数暂时储存起来
  43. // 二分查找找到应该插入的下标
  44. int final_local = find(arr, i);
  45. // 该右移的右移
  46. for (int j = i - 1; j >= final_local; j--)
  47. {
  48. arr[j + 1] = arr[j];
  49. }
  50. arr[final_local] = temp;
  51. }
  52. }
  53. int main()
  54. {
  55. int arr[] = { 9,6,8,-1,0,5,2,8 };
  56. size_t sz = sizeof(arr) / sizeof(arr[0]);
  57. sort(arr, sz); // 插入排序优化
  58. for (int i = 0; i < sz; i++)
  59. {
  60. printf("%d ", arr[i]);
  61. }
  62. return 0;
  63. }

4. 快速排序 

4.1 原理

核心思想是分治法一分为二,左边比某个基准数小,右边比某个基准数大,左边右边又一分为二,直至不可再分

 

4.2 具体步骤

  1. 从数列中随便挑一个数作为基准数(我选的是最后一个数);

  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。

  3. 递归地把小于基准值元素的子数列和大于基准值元素的子数列排序;

4.3 代码实现 

关键点:递归的思想,函数要被调用,要写得一般一些

  1. #include<stdio.h>
  2. /*
  3. 函数功能:将最后一个元素作为基准数
  4. 小于它的放左边,大于它的放右边
  5. 返回值:基准数最后的位置
  6. */
  7. int partition(int arr[], int start, int end)
  8. {
  9. // 遍历基准元素前的所有元素
  10. for (int i = end - 1; i >= start; i--)
  11. {
  12. if (arr[i] > arr[end])
  13. {
  14. int temp = arr[i];
  15. arr[i] = arr[end - 1];
  16. arr[end - 1] = arr[end];
  17. arr[end] = temp;
  18. end -= 1;
  19. }
  20. }
  21. return end;
  22. // 2 1 3 2
  23. }
  24. void QuickSort(int arr[], int start, int end)
  25. {
  26. if (start < end)
  27. {
  28. // 函数最后返回的是排过后基准元素的位置
  29. int pivot = partition(arr, start, end);
  30. // 递推式的一分为二
  31. QuickSort(arr, start, pivot - 1);
  32. QuickSort(arr, pivot + 1, end);
  33. }
  34. }
  35. int main()
  36. {
  37. int arr[] = { 9,6,8,-1,0,5,2,8 };
  38. size_t sz = sizeof(arr) / sizeof(arr[0]);
  39. // 以后还要调用,需写得一般一些
  40. QuickSort(arr, 0, sz - 1); // 快速排序
  41. for (int i = 0; i < sz; i++)
  42. {
  43. printf("%d ", arr[i]);
  44. }
  45. return 0;
  46. }

4.4 优化 

快速排序的效率在于其平均时间复杂度为O(nlogn),这使其成为实际应用中非常受欢迎的一种排序算法。然而,在最坏的情况下,其时间复杂度会退化到O(n^2),这通常发生在每次选择的基准都是最大或最小元素时。为了避免这种情况,可以采用随机选择基准或者三数取中等策略来优化快速排序的性能。下面演示随机选择的优化

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<time.h>
  4. /*
  5. 函数功能:将最后一个元素作为基准数
  6. 小于它的放左边,大于它的放右边
  7. 返回值:基准数最后的位置
  8. */
  9. int partition(int arr[], int start, int end)
  10. {
  11. int end_temp = end;
  12. end = rand() % (end - start) + start + 1;
  13. // 把左边大的甩到右边
  14. for (int i = end - 1; i >= start; i--)
  15. {
  16. if (arr[i] > arr[end])
  17. {
  18. int temp = arr[i];
  19. arr[i] = arr[end - 1];
  20. arr[end - 1] = arr[end];
  21. arr[end] = temp;
  22. end -= 1;
  23. }
  24. }
  25. // 把右边小的甩到左边
  26. for (int i = end + 1; i <= end_temp; i++)
  27. {
  28. if (arr[i] < arr[end])
  29. {
  30. int temp = arr[i];
  31. arr[i] = arr[end + 1];
  32. arr[end + 1] = arr[end];
  33. arr[end] = temp;
  34. end += 1;
  35. }
  36. }
  37. return end;
  38. // 2 1 3 2
  39. }
  40. void QuickSort(int arr[], int start, int end)
  41. {
  42. if (start < end)
  43. {
  44. // 函数最后返回的是排过后基准元素的位置
  45. int pivot = partition(arr, start, end);
  46. // 递推式的一分为二
  47. QuickSort(arr, start, pivot - 1);
  48. QuickSort(arr, pivot + 1, end);
  49. }
  50. }
  51. int main()
  52. {
  53. srand((unsigned int)time(NULL));
  54. int arr[] = { 9,6,8,-1,0,5,2,8 };
  55. size_t sz = sizeof(arr) / sizeof(arr[0]);
  56. // 以后还要调用,需写得一般一些
  57. QuickSort(arr, 0, sz - 1); // 快速排序
  58. for (int i = 0; i < sz; i++)
  59. {
  60. printf("%d ", arr[i]);
  61. }
  62. return 0;
  63. }

感谢观看!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/233266
推荐阅读
相关标签
  

闽ICP备14008679号