当前位置:   article > 正文

交换排序的两种方法

交换排序的两种方法

C语言实现交换排序的两种方法:冒泡排序和快排。

冒泡排序:冒泡排序十分简单,在这里简要分析:

算法步骤:

比较相邻的元素。如果第一个比第二个大,就交换他们两个。

对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

针对所有的元素重复以上的步骤,除了最后一个。

持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

 冒泡排序的特性:

冒泡排序的特性总结:
1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:稳定
 

  1. void Swap(int* e1, int* e2)
  2. {
  3. int tmp = *e1;
  4. *e1 = *e2;
  5. *e2 = tmp;
  6. }
  7. void BubbleSort(int* arr, int len)
  8. {
  9. int i = 0;
  10. for (i = 0; i < len; i++)
  11. {
  12. for (int j = 0; j < len - i - 1; j++)
  13. {
  14. //交换两个数的大小
  15. if (arr[j] > arr[j + 1])
  16. Swap(&arr[j], &arr[j + 1]);
  17. }
  18. }
  19. }

下面讲解快排

快排基本思想:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

快排有递归和非递归两种写法,先来讲解递归的方法

快速排序的特性总结:
1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(logN)
4. 稳定性:不稳定
 

递归的有三种写法:基本法,挖坑法,指针法。

基本法:基本法就是建立一个基准值,基准值可以随机选取,但随机选取的可能是数组中最大值或者是数组中的最小值,所以最好的方法是三目取中法,就是选取数组左右两端的数据和中间的数据,然后判断这三个数中那个是中间值,然后将这个数有最左端或最右端的数交换,这样做的目的是为了减小排序的次数。然后就是一趟排序了。

基本法就是基准值在左边的时候,右边先走然后再走左边,当右边的数小于基准值,左边的数大于基准值使,交换左右两边的数。

 代码如下:

  1. //排序一趟的方法
  2. //交换数据
  3. void Swap(int* e1, int* e2)
  4. {
  5. int tmp = *e1;
  6. *e1 = *e2;
  7. *e2 = tmp;
  8. }
  9. //三目取中法
  10. int GetMinNum(int* arr, int left, int right)
  11. {
  12. int x = left;
  13. int y = right;
  14. int z = (x + y) / 2;
  15. int max = arr[x];
  16. int min = arr[left];
  17. int sum = x + y + z;
  18. if (arr[x] > arr[y])
  19. max = x;
  20. else
  21. max = y;
  22. if (arr[max] < arr[z])
  23. max = z;
  24. if (arr[y] > arr[x])
  25. min = x;
  26. else
  27. min = y;
  28. if (arr[min] > arr[z])
  29. min = z;
  30. return sum-max-min;
  31. }
  32. //基础法
  33. int Partion1(int* arr, int left, int right)
  34. {
  35. int key = GetMinNum(arr, left, right);
  36. //int key = left;
  37. //Swap(&key, &arr[left]);
  38. while (left < right)
  39. {
  40. //左右两边的数交换
  41. while (left < right && arr[right] >= arr[key])
  42. {
  43. right--;
  44. }
  45. while (left < right && arr[left] <= arr[key])
  46. {
  47. left++;
  48. }
  49. Swap(&arr[left], &arr[right]);
  50. }
  51. //右边的数有基准值交换
  52. Swap(&arr[left], &arr[key]);
  53. //返回两边相遇的数的下标
  54. return left;
  55. }

 2,挖坑法

挖坑法和基础法差不多,假设基准值在左边,则将基准值保留下来,然后右边的指针找比基准值小的数,左边的找比基准值大的数,然后交换,交换的地方变成一个新坑,具体看代码

  1. //交换数据
  2. void Swap(int* e1, int* e2)
  3. {
  4. int tmp = *e1;
  5. *e1 = *e2;
  6. *e2 = tmp;
  7. }
  8. //挖坑法,排一边的时候
  9. int Partion2(int* arr, int left, int right)
  10. {
  11. int key = left;
  12. int dig = key;
  13. while (left < right)
  14. {
  15. while (left < right && arr[right] >= arr[key])
  16. {
  17. right--;
  18. }
  19. while (left < right && arr[left] <= arr[key])
  20. {
  21. left++;
  22. }
  23. Swap(&arr[left], &arr[right]);
  24. }
  25. Swap(&arr[left], &arr[dig]);
  26. return left;
  27. }

然后是指针法

指针法就是定义一个前后指针,假设基准值在左边,在起始位置,后指针比前指针多一步,(如果基准值在右边,则后指针在数组起始位置,后指针为空),然后后指针找小,找到比基准值小的数之后前指针加一之后与后指针交换,如果后指针走到结尾,则结束。

  1. //交换函数
  2. void Swap(int* e1, int* e2)
  3. {
  4. int tmp = *e1;
  5. *e1 = *e2;
  6. *e2 = tmp;
  7. }
  8. int Partion3(int* arr, int left, int right)
  9. {
  10. int key = left;
  11. int cur = left + 1;
  12. int pre = left;
  13. while (cur <= right)
  14. {
  15. /*while (arr[cur] >= arr[key] && cur <= right)
  16. {
  17. cur++;
  18. }
  19. if(cur<=right)
  20. Swap(&arr[cur], &arr[++pre]);*/
  21. if (arr[cur] < arr[key] && arr[++pre] != arr[cur])
  22. {
  23. Swap(&arr[cur], &arr[pre]);
  24. }
  25. cur++;
  26. }
  27. Swap(&arr[key], &arr[pre]);
  28. return pre;
  29. }

完整代码

  1. //快排
  2. //基础版
  3. //交换函数
  4. void Swap(int* e1, int* e2)
  5. {
  6. int tmp = *e1;
  7. *e1 = *e2;
  8. *e2 = tmp;
  9. }
  10. //三目取中法
  11. int GetMinNum(int* arr, int left, int right)
  12. {
  13. int x = left;
  14. int y = right;
  15. int z = (x + y) / 2;
  16. int max = arr[x];
  17. int min = arr[left];
  18. int sum = x + y + z;
  19. if (arr[x] > arr[y])
  20. max = x;
  21. else
  22. max = y;
  23. if (arr[max] < arr[z])
  24. max = z;
  25. if (arr[y] > arr[x])
  26. min = x;
  27. else
  28. min = y;
  29. if (arr[min] > arr[z])
  30. min = z;
  31. return sum-max-min;
  32. }
  33. int Partion1(int* arr, int left, int right)
  34. {
  35. int key = GetMinNum(arr, left, right);
  36. //int key = left;
  37. Swap(&key, &arr[left]);
  38. while (left < right)
  39. {
  40. //左右两边的数交换
  41. while (left < right && arr[right] >= arr[key])
  42. {
  43. right--;
  44. }
  45. while (left < right && arr[left] <= arr[key])
  46. {
  47. left++;
  48. }
  49. Swap(&arr[left], &arr[right]);
  50. }
  51. //右边的数有基准值交换
  52. Swap(&arr[left], &arr[key]);
  53. //返回两边相遇的数的下标
  54. return left;
  55. }
  56. //挖坑法
  57. int Partion2(int* arr, int left, int right)
  58. {
  59. int key = left;
  60. int dig = key;
  61. while (left < right)
  62. {
  63. while (left < right && arr[right] >= arr[key])
  64. {
  65. right--;
  66. }
  67. while (left < right && arr[left] <= arr[key])
  68. {
  69. left++;
  70. }
  71. Swap(&arr[left], &arr[right]);
  72. }
  73. Swap(&arr[left], &arr[dig]);
  74. return left;
  75. }
  76. //指针法
  77. int Partion3(int* arr, int left, int right)
  78. {
  79. int key = left;
  80. int cur = left + 1;
  81. int pre = left;
  82. while (cur <= right)
  83. {
  84. /*while (arr[cur] >= arr[key] && cur <= right)
  85. {
  86. cur++;
  87. }
  88. if(cur<=right)
  89. Swap(&arr[cur], &arr[++pre]);*/
  90. if (arr[cur] < arr[key] && arr[++pre] != arr[cur])
  91. {
  92. Swap(&arr[cur], &arr[pre]);
  93. }
  94. cur++;
  95. }
  96. Swap(&arr[key], &arr[pre]);
  97. return pre;
  98. }
  99. void QuickSort(int* arr, int left, int right)
  100. {
  101. if (left >= right)
  102. return;
  103. int key1 = Partion1(arr, left, right);
  104. int key1 = Partion2(arr, left, right);
  105. int key1 = Partion3(arr, left, right);
  106. QuickSort(arr, left, key1 - 1);
  107. QuickSort(arr, key1 + 1, right);
  108. }

递归与二叉树前序遍历规则非常像,同学们在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

非递归

非递归的思想与递归的思想几乎一样,只是方法不同,非递归的方法时间复杂度为O(N*logn)

非递归是由来排序数组元素出项大量重复的时候使用的,因为大量重复元素导致操作系统栈溢出,递归无法完成。

非递归可以用栈或者队列来实现,我们这里用栈来实现快排的非递归写法,由于我们使用C语言来写,所以需要自己写一个栈出来,具体实现看代码

  1. //实现栈
  2. #define _CRT_SECURE_NO_WARNINGS
  3. #include "Stack.h"
  4. //初始化栈
  5. void STListInIt(ST* ps)
  6. {
  7. ps->a = (STypeDate*)malloc(sizeof(STypeDate) * 4);
  8. if (ps->a == NULL)
  9. {
  10. perror("malloc fail");
  11. return;
  12. }
  13. ps->top = 0;
  14. ps->capacity = 4;
  15. }
  16. //销毁栈
  17. void STListDestroy(ST* ps)
  18. {
  19. free(ps->a);
  20. ps->a = NULL;
  21. ps->top = 0;
  22. ps->capacity = 0;
  23. }
  24. //插入
  25. void STListPush(ST* ps, STypeDate x)
  26. {
  27. if (ps->top == ps->capacity)
  28. {
  29. STypeDate* pps = realloc(ps->a, sizeof(STypeDate) * ps->capacity * 2);
  30. if (pps == NULL)
  31. {
  32. perror("realloc file");
  33. return;
  34. }
  35. //ps->a = pps;
  36. ps->capacity *= 2;
  37. }
  38. ps->a[ps->top] = x;
  39. ps->top++;
  40. }
  41. //删除
  42. void STListPop(ST* ps)
  43. {
  44. assert(ps);
  45. ps->top--;
  46. }
  47. //计算栈的大小
  48. int STListSize(ST* ps)
  49. {
  50. assert(ps);
  51. assert(!STListEmpty(ps));
  52. return ps->top;
  53. }
  54. //判断是否为空
  55. bool STListEmpty(ST* ps)
  56. {
  57. assert(ps);
  58. return ps->top == 0;
  59. }
  60. //出栈元素
  61. STypeDate STListTop(ST* ps)
  62. {
  63. assert(ps);
  64. assert(!STListEmpty(ps));
  65. return ps->a[ps->top - 1];
  66. }
  67. //交换函数
  68. void Swap(int* e1, int* e2)
  69. {
  70. int tmp = *e1;
  71. *e1 = *e2;
  72. *e2 = tmp;
  73. }
  74. //非递归
  75. void QuickSortNonR(int* arr, int left, int right)
  76. {
  77. ST ps;
  78. STListInIt(&ps);
  79. STListPush(&ps, left);
  80. STListPush(&ps, right);
  81. while (!STListEmpty(&ps))
  82. {
  83. int end = STListTop(&ps);
  84. STListPop(&ps);
  85. int begin = STListTop(&ps);
  86. STListPop(&ps);
  87. int key = Partion2(arr, begin, end);//排列一次的函数可以用上面的三个函数,任选其一
  88. if (key + 1 < end)
  89. {
  90. STListPush(&ps, key + 1);
  91. STListPush(&ps, end);
  92. }
  93. if (key - 1 > begin)
  94. {
  95. STListPush(&ps, begin);
  96. STListPush(&ps, key - 1);
  97. }
  98. }
  99. STListDestroy(&ps);
  100. }

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

闽ICP备14008679号