当前位置:   article > 正文

六大常见排序算法(插入、堆排、希尔、选择、冒泡、快速)_六大排序

六大排序

多多重复,百炼成钢!!!

文章目录:

  • 一、插入排序

  • 二、堆排序

  • 三、希尔排序

  • 四、选择排序

  • 五、冒泡排序

  • 六、快速排序

  • 总结

排序的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

一、直接插入排序

时间复杂度(最坏情况):O(N^2)

空间复杂度:O(1)

稳定性:好

  1. // 最坏时间复杂度O(N^2)-逆序
  2. void InsertSort(int* a, int n)
  3. {
  4. // 在[0,end]之间 插入 end+1,保持 [0, end+1]有序
  5. for (int i = 0; i < n - 1; ++i)
  6. {
  7. int end = i;
  8. int tmp = a[end + 1];
  9. while (end >= 0)
  10. {
  11. if (a[end] > tmp)
  12. {
  13. a[end + 1] = a[end];
  14. --end;
  15. }
  16. else
  17. {
  18. break;
  19. }
  20. }
  21. a[end + 1] = tmp;
  22. }
  23. }

二、堆排序

时间复杂度:O(N*logN)

空间复杂度:O(N)

稳定性:不好

 

 要求升序-建大堆
 要求降序-建小堆

步骤:
 1.先找到最后一个节点,然后找到他的父亲a,在a在的堆(无序),通过向下调整把该堆调整为有序(大堆或小堆)
 2. 通过 i-- 找到父亲a的前一个节点b(也是另外一个堆的父亲-b),然后在父亲b所在的堆(无序)向下调整为有序
 3.迭代往根方向往上走-最后整个堆都有序(大堆或小堆)

  1. //最后一层排好的调整次数N/2*logN 总:O(N*logN)
  2. void Heapsort(int* a, int n)
  3. {
  4. for (int i = (n - 1-1) / 2; i >= 0; --i)//n-1为最后一个节点的下标,最后一个节点的父亲下标:(最后一个节点的下标-1)/2
  5. {
  6. Adjustdown(a, n, i);
  7. }
  8. int i = 1;
  9. while (i < n)
  10. {
  11. swap(&a[0], &a[n - i]);//把最大的放最后,然后重新排,迭代把最大的头插到后面,排(n-1)次最后排成升序
  12. Adjustdown(a, n - i, 0);
  13. ++i;
  14. }
  15. }

向下调整

  1. void Adjustdown(HPDatatype* a, int n, int parent)//向下调整-O(logN)
  2. {
  3. int minchild = 2 * parent + 1;
  4. while (minchild < n)
  5. {
  6. if (minchild + 1 < n && a[minchild + 1] > a[minchild])
  7. {
  8. minchild++;
  9. }
  10. if (a[minchild] > a[parent])
  11. {
  12. swap(&a[minchild], &a[parent]);
  13. parent = minchild;
  14. int minchild = 2 * parent + 1;
  15. }
  16. else
  17. {
  18. break;
  19. }
  20. }
  21. }

三、希尔排序

时间复杂度:O(N^1.3)

空间复杂度:O(1)

稳定性:不好

gap越大,直接插入排序就越快,反而不那么有序
gap越小,直接插入排序就越慢,反而更有序

步骤: 

(gap为1的时候为直接插入排序;其余gap>1时为预排序)

1.预排序-接近有序-间隔为gap的数据分为一组,插入排序
2.直接插入排序

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

四、选择排序

时间复杂度:O(N^2)

空间复杂度:O(1)

稳定性:不好

思路:(要求升序)把第一个元素设为最小值从这里开始找,遍历一轮数组,若找到比最小值还小的元素(遍历元素里面最小的)则交换;

然后把第二个元素作为最小值从这里开始找,遍历......以此类推,到最后一个元素为止

  1. void SelectSort(int* a, int n)//选择排序-O(N*2)
  2. {
  3. int begin = 0;
  4. int end = n - 1;
  5. //选出最小的放begin位置
  6. //选出最大的放end位置
  7. //1.选出最大的数和最小的数
  8. while (begin < end)
  9. {
  10. int minii = begin;
  11. int maxi = begin;
  12. for (int i = begin + 1; i <= end; ++i)
  13. {
  14. if (a[i] > a[maxi])
  15. {
  16. maxi = i;
  17. }
  18. if (a[i] < a[minii])
  19. {
  20. minii = i;
  21. }
  22. }
  23. //2.把最大的数放end位置,把最小的数放begin位置
  24. swap(&a[begin], &a[minii]);//最小的数放在begin位置
  25. if (maxi == begin)//如果一开始最大的数就在begin位置则这样判断更改bug
  26. {
  27. maxi = minii;
  28. }
  29. swap(&a[end], &a[maxi]);//最大的数放在2end位置
  30. //3.范围缩小【begin,end】->【begin+1,end—1】
  31. ++begin;
  32. --end;
  33. }
  34. }

五、冒泡排序

时间复杂度-最坏(O(N^2)) 最好(O(N))

空间复杂度:O(1)

稳定性:好

步骤:
升序-把最大的放最后
降序-把最小的放最后

  1. void BubbleSort(int* a, int n)
  2. {
  3. for (int j = 0; j < n; j++)
  4. {
  5. int exchange = 0;//优化:如果此时顺序符合升序或降序,则跳出循环
  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 = 1;
  12. }
  13. }
  14. if (exchange == 0)
  15. {
  16. break;
  17. }
  18. }
  19. }

六、快速排序

A、递归实现

1.优化:三数取中:三个数当中取个数值大小为中间值的值

  取出来的值作为keyi

  1. int Getmidindex(int* a, int left, int right)//三数取中
  2. {
  3. int mid = left + (right - left) / 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. }

霍尔排序:

R找比keyi小的值,找到则停下; L找比keyi大的值,找到则停下;   然后L和R交换,依次到最后碰面,把keyi和碰面时的值交换,完成排序。

最后返回碰面的下标

  1. int PartSort(int* a, int left, int right)//霍尔排序
  2. {
  3. int mid = Getmidindex(a, left, right);
  4. swap(&a[mid], &a[left]);
  5. int keyi = left;
  6. while (left < right)
  7. {
  8. //R找小
  9. while (left < right && a[right] >= a[keyi])
  10. {
  11. --right;
  12. }
  13. //L找大
  14. while(left < right && a[left] <= a[keyi])
  15. {
  16. ++left;
  17. }
  18. if (left < right)
  19. swap(&a[left], &a[right]);
  20. }
  21. int meeti = left;
  22. swap(&a[keyi], &a[meeti]);
  23. return meeti;
  24. }

挖坑法 

步骤:

先把key(left)所在位置作为坑位;

然后R先走,找到比keyi小的值则把该值填到坑位,然后该位置为新的坑位;

然后L走,找到比keyi大的值则把该值填到坑位,然后形成新的坑位;

以此类推,最后碰面时作为最后的坑位,把keyi填进去;

最后返回最后坑位的下标

  1. int PartSort2(int* a, int left, int right)//挖坑法
  2. {
  3. int mid = Getmidindex(a, left, right);
  4. swap(&a[mid], &a[left]);
  5. int key = a[left];
  6. int hole = left;
  7. while (left < right)
  8. {//一开始left为hole
  9. //R找小,找到小后就把值填到hole所在的坑,填完就为hole
  10. while (left < right && a[right] >= key)
  11. {
  12. --right;
  13. }
  14. a[hole] = a[right];
  15. hole = right;
  16. //L找大,找到大后就把值填到hole所在的坑,填完就为hole
  17. while (left < right && a[left] <= key)
  18. {
  19. ++left;
  20. }
  21. a[hole] = a[left];
  22. hole = left;
  23. }
  24. a[hole] = key;
  25. return hole;
  26. }

前后指针法:

步骤:

cur和prev一起走:如果cur遇到比keyi大的值则prev停下,则cur继续走,之后若遇到比keyi小的值则该值与prev所在的值交换;

以此类推,cur越界后prev停下,此时把prev所在值与keyi交换;

最后返回prev所在的下标

  1. int PartSort3(int* a, int left, int right)//前后指针法
  2. {
  3. int mid = Getmidindex(a, left, right);
  4. swap(&a[mid], &a[left]);
  5. int keyi =left;
  6. int prev = left;
  7. int cur = left+1;
  8. while (cur<=right)
  9. {//cur找比key大的值之后的小
  10. if (a[cur] < a[keyi] && ++prev != cur)//prev要和cur有间隔
  11. swap(&a[cur], &a[prev]);
  12. ++cur;
  13. }
  14. swap(&a[keyi], &a[prev]);
  15. return prev;
  16. }

以上三种方法都是一轮排序:并不能完全排好,但能把小的值放在左边,大的值放在右边(若升序);则需要用到递归依次排序好

注:一般情况下 一轮排序最多可把8个值完全排好顺序

总排序-递归(快速排序)


停止排序: 要么剩下一个元素 要么元素不存在(越界)
 时间复杂度:
 无序O(N*logN)
 有序O(N*N)

空间复杂度:O(1)

稳定性:不好

  1. void QuickSort(int* a, int begin, int end)//单趟-三个区间-2^3=8(最多八个数排序好)
  2. {
  3. if (begin >= end)
  4. {
  5. return;
  6. }
  7. if (end - begin <= 8)//小区间优化:最后一趟(层)用递归消耗大-直接换插入排序
  8. {
  9. InsertSort(a + begin, end - begin + 1);
  10. }
  11. else {
  12. 先对区间[begin,end]进行排序
  13. // int keyi = PartSort(a, begin, end);//霍尔(hoare)排序
  14. // int keyi = PartSort2(a, begin, end);//挖坑法
  15. int keyi = PartSort3(a, begin, end);//前后指针法
  16. //经过上面排序好之后可分为区间:[begin,keyi-1],keyi,[keyi+1,end]
  17. //则先对区间:[begin,keyi-1]排序
  18. // PartSort(a, begin, keyi - 1);//霍尔排序
  19. // PartSort2(a, begin, keyi - 1);//挖坑法
  20. PartSort3(a, begin, keyi - 1);//前后指针法
  21. //再对区间:[keyi+1,end]排序
  22. //PartSort(a, keyi + 1, end);//霍尔排序
  23. // PartSort2(a, keyi + 1, end);//挖坑法
  24. PartSort3(a, keyi + 1, end);//前后指针法
  25. }
  26. }

B、非递归实现

栈实现

思路:

若需要需要把数组[9,8,7,6,5,4,3,2,1,0]进行升序排列:

先把下标为0 9push入栈(出栈顺序为9 0 - 对应区间为[0,9]);

然后取left=0 right=9 并且都pop掉 然后对该区间[left,right]进行排序;

然后分为两个区间[0,4] 5 [6,9];

然后把下标为 6 9push入栈,再把 下标为 0 4 push入栈 ;

然后取left=0,right=9 并且都pop掉然后对该区域[left,right]进行排序;

然后再分区间......

以此类推

停止排序: 要么剩下一个元素 要么元素不存在(越界)

  1. void QuickSortNonR(int* a, int begin, int end)
  2. {
  3. ST st;
  4. StackInit(&st);
  5. StackPush(&st, begin);//放0
  6. StackPush(&st, end);//放9
  7. //先出9后出0 则范围为[0,9]则是先对[0,9]范围排序
  8. while (!StackEmpty(&st))
  9. {
  10. int right = StackTop(&st);
  11. StackPop(&st);
  12. int left = StackTop(&st);
  13. StackPop(&st);
  14. int keyi = PartSort3(a, left, right);
  15. //[left,keyi-1] keyi [keyi+1,right]
  16. if (keyi + 1 < right)//然后先push右边区间
  17. {
  18. StackPush(&st,keyi+1);
  19. StackPush(&st, right);
  20. }
  21. if (left < keyi - 1)//再push左边区间
  22. {
  23. StackPush(&st, left);
  24. StackPush(&st, keyi - 1);
  25. }
  26. }
  27. StackDestory(&st);
  28. }


总结

以上六大排序都属于内排序(在内存中中排序),他们非常常见且实用,你也一定要掌握好噢!!!

如果以上内容能对你有帮助的话,麻烦给我一件三联!!!(超大声

不许下次一定(doge

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

闽ICP备14008679号