1.常用排序算法介绍

一个排序算法的好坏需要针对它在某个场景下的时间复杂度和空间复杂度来进行判断、并且排序都需要求其稳定性,比如排序之前a在b前面,且a=b,排序之后也要保持a在b前面、

常用排序算法如下所示:

2.选择排序

首先i等于0,每次i++,并从i轮询到末尾,选择一个最小者与第i个位置进行交换.

比如:

16,8,21,10,49

第1次遍历整个数组,找到最小值8,则与arr[0]进行交换:

8,16,21,10,49

第2次遍历下标1~4的数组,找到10,则与arr[1]进行交换:

8,10 ,21,16,49

第3次遍历下标2~4的数组,找到16,则与arr[2]进行交换:

8,10 , 16, 21,49

第4次遍历下标3~4的数组,找到21,由于交换的位置相等,则保持不变

由于第5次,起始小标等于n-1了.所以已经比较完了.则结束了.

所以代码如下所示:

template <typename T>
void mySwap(T& a, T& b)    // 交换函数
{T c(a);a = b;b = c;
}template <typename T>
void seleteSort(T arr[], int len, bool ascend = true)      // ascend为ture表示升序
{for (int i = 0; i < len -1; ++i) {int minIdx = i;for (int j = i+1; j < len; ++j) {if (ascend ? (arr[minIdx] > arr[j]) : (arr[minIdx] < arr[j])) {minIdx = j;}}if (minIdx != i) {mySwap(arr[i], arr[minIdx]);}}
}

测试代码如下所示:

    int arr[8] = {25, 22, 12, 8, 9, 11, 30 ,40};bubbleSort(arr, 8);cout<<"ascend: "<<endl;for (int i = 0; i < 8; ++i) {cout<<i<<": "<<arr[i]<<endl;}bubbleSort(arr, 8, false);cout<<"descend: "<<endl;for (int i = 0; i < 8; ++i) {cout<<i<<": "<<arr[i]<<endl;}

打印如下所示:

总结:

选择排序是不稳定的排序算法,例如 [3, 4, 3, 1, 5]这个数组,第一次交换,第一个3和1交换位置,此时原来两个3的相对位置发生了变化,所以无法保证值相等的元素的相对位置不变,

3.冒泡排序

重复len次冒泡,每次冒泡都是(int j=len-1; j>i; j--)从数组末尾往前进行冒泡,如果arr[j-1]>arr[j],则将arr[j]与arr[j-1]交换,让值小的冒泡上去.

代码如下所示:

template < typename T >
static void bubbleSort(T arr[], int len, bool ascend = true)
{bool isSwap = true;for(int i=0; (i<len) && isSwap; i++)    // 每次冒泡时,则判断上次是否发生过交换,如果交换过,那说明还不是个有序的表{isSwap = false;     // 每次冒泡的时候复位 “交换标志位”for(int j=len-1; j>i; j--){if( ascend ? ( arr[j-1] > arr[j] ) : (arr[j-1] < arr[j] ) ) // 如果是升序,arr[j-1] > arr[j],则将arr[j]从末尾冒泡冒上去{mySwap(arr[j], arr[j-1]);isSwap = true;     // “交换标志位” 设置为true}}}
}

PS: 由于mySwap()函数和测试代码已经写过了,后面就不再重复写了。

总结:

冒泡排序是稳定的排序算法,因为每次都是冒泡相邻比较,比如升序,如果后者>=前者,是不会进行交换

4.插入排序

将一个数组分为两个部分,左边是排序好的,右边是未排序的.

然后每次提取一个右边的数据出来,然后轮询左边(排序好的),如果找到合适的位置(前者小于它且后者大于它),则插入进去.如果未找到,则插入在左边排序好的末尾位置.

比如16,8,21,10,49,由于左边默认会有一个数值(一个数值无需判断比较),所以无需判断arr[0]:

16  |  8,21,10,49  ( |左边是排序好的,右边是等待排序的 )

第1次,获取arr[1],由于8小于16,插入在16前面,所以插入后:

8,16  |  21,10,49

第2次,获取arr[2],向前轮询,由于21>16,所以停止向前遍历, 并且位置没改变过,所以无需操作:

8,16,21  |  10,49

第3次,获取arr[3], 向前轮询,由于10<21,则21向后挪位,再继续向前轮询,由于10<16,则16向后挪位,最后10>=8,则插入在8后面:

8, 10,16,21  |  49

第4次,获取arr[4],由于49>21,所以停止向前遍历, 并且位置没改变过,所以无需操作最终为:

8, 10,16,21 ,49

代码如下所示:

template < typename T >
static void insertSort(T arr[], int len, bool ascend = true)
{for(int i=1; i<len; i++) // 循环取出右边未排序的{int k = i;T temp = arr[i];      // 把值取出来,后面会进行挪位操作,会覆盖掉arr[i]原来的内容// 如果ascend为true,则表示升序,那么当后者大于前者时,则停止for轮询,因为左边是有序序列for(int j=i-1; (j>=0) && (ascend ? (arr[j]>temp) : (arr[j]<temp)); j--)    {arr[j+1] = arr[j];   // 进行先后挪位操作k = j;               // 更新要插入的位置}if( k != i )          // 如果未进行挪位,那么k==i,则不需要重复插入{arr[k] = temp;}}
}

总结:

插入排序是稳定的排序算法,例如 [4,3, 3, 1, 5]这个数组,由于左边的3是提前插入的[ 3, 4 3, 1, 5],而后续的3由于>=前面的3,所以插入在后面

5.希尔排序

希尔排序是插入排序的升级版.并且比插入排序快.

希尔排序则是将一个插入排序,比如初始步长间隔gap为3,那么将会分成3个小组进行插入排序,每个小组插入排序好后,再次将步长间隔gap较少一部分再次进行插入排序,直到最后步长偏移为1后,进行整个插入排序.一般gap步长都是以 len/3+1来计算.以后每次以 gap/3+1来缩小。

PS: 也可以设置gap固定增量从 n/2 开始,以后每次缩小到原来的一半。

比如下图所示:

代码如下所示:

template < typename T >
static void shellSort(T arr[], int len, bool ascend = true)
{int gap = len;while (gap > 1) {gap = gap / 3 + 1;      // 加一能够保证gap最后一次为1.for(int offset = 0 ; offset < gap; ++offset) {      // 对每组进行插入排序for(int i=gap-offset; i<len; i+=gap) // 循环取出右边未排序的{int k = i;T temp = arr[i];        // 把值取出来,后面会进行挪位操作,会覆盖掉arr[i]原来的内容// 假如ascend为true,则表示升序,那么当后者大于前者时,则停止for轮询for(int j=i-gap; (j>=0) && (ascend ? (arr[j]>temp) : (arr[j]<temp)); j-=gap)     {arr[j+gap] = arr[j];   // 进行先后挪位操作k = j;                // 更新要插入的位置}if( k != i )          // 如果未进行挪位,那么k==i,则不需要重复插入{arr[k] = temp;}}}}
}

总结:

希尔排序是不稳定的排序算法,因为希尔排序通过分组方法,可能会将最后的值跳过中间重复的值排序到前面

6.归并排序

归并是将两个或多个有序序列合并成一个新的有序序列。归并方法有多种,一次对两个有序记录序列进行归并,称为二路归并排序,也有三路归并排序多路归并排序。本代码是二路归并排序.

思路如下所示:

通过递归的方式将一个无序序列,对半拆解、直到不能拆解时,则进行两路比较排序(归并)到一个辅助空间数组中,归并完成后则将辅助数组中内容拷贝回去,返回上一级递归再进行两路比较排序(归并)和拷贝.直到最后则整个序列归并完成.

如下图所示(红色数字表示递归拆解和归并的先后顺序):

代码如下所示:

template < typename T >
static void merge(T arr[], int l, int mid, int r, bool ascend = true)    // 进行比较归并
{int n = r - l + 1;T* helper = new T[n];          // 创建一个辅助空间,用来临时存储int i = 0;int left = l;int right = mid+1;while (left <= mid && right <= r) {// 如果ascend = true, 则判断左边如果大于右边的,则存放右边的if (ascend ? arr[left] > arr[right] : arr[left] < arr[right])helper[i++] = arr[right++];elsehelper[i++] = arr[left++];}// 将某一侧剩余的存放进去while (left <= mid)helper[i++] = arr[left++];while (right <= r)helper[i++] = arr[right++];// 最后将排序好的替换到原数组for (i = 0; i < n; ++i) {arr[l + i] = helper[i];}delete [] helper;}template < typename T >
static void mergeSort(T arr[], int l, int r, bool ascend = true)    // 递归拆解
{if (l >= r)      // 不能再进行拆解了,直接退出return;int mid = (l+r)/2;mergeSort(arr, l, mid, ascend);mergeSort(arr, mid+1, r, ascend);merge(arr, l, mid, r, ascend);}template < typename T >
static void mergeSort(T arr[], int len, bool ascend = true)
{mergeSort(arr, 0, len-1, ascend);
}

总结:

由于归并排序需要使用一个辅助空间,所以空间复杂度为O(n),由于都是附近的两两比较,所以是个稳定的算法.

7.快速排序

快速排序是冒泡排序的一种改进,主要的算法思想是在待排序的 n 个数据中取第一个数据作为Pivot基准值,然后采用二分的思想,把大于Pivot的数据放在右边,把小于Pivot的数据放在左边最后确定Pivot基准值的中间位置.然后再次递归进行左右分区确定基准值,直到左右两边都是一个数值时,则完成排序.

如下图所示:

步骤如下:

1)  设置两个变量l、r,排序开始的时候:l=0,r=n-1;
2)第一个数组值作为Pivot基准值,pivot=arr[0];
3)然后r-- ,向左搜索,找到小于pivot后,因为arr[l]的值保存在pivot中,所以直接赋值,s[l]=s[r]
4)然后l++,向右搜索,找到大于pivot后,因为arr[r]的值保存在第2步的s[l]中,所以直接赋值,s[r]=s[l],然后r--,避免死循环
5)重复第3、4步,直到l=r,最后将pivot值放在arr[l]中(确定了位置)
6)  然后采用“二分”的思想,以i为分界线,拆分成两个数组 s[0,l-1]、s[l+1,n-1]又开始排序

代码如下所示:

template < typename T >
static void quickSort(T arr[], int left, int right,  bool ascend = true)
{if (left>=right)return;int l =left, r = right;T pivot = arr[l];           // 默认将最左边的设置为基值while(l<=r) {while(r > l && (ascend ? arr[r] >= pivot : arr[r] <= pivot)) r--;arr[l] = arr[r];       // 由于arr[l]已经存放在pivot了,所以直接替换while(l < r && (ascend ? arr[l] <= pivot: arr[l] >= pivot)) l++;arr[r--] = arr[l];     // 由于arr[r]之前的值已经存在了左边,所以直接替换}arr[l]=pivot;             // 最后找到基值的位置quickSort(arr, left, l-1, ascend);quickSort(arr, l+1, right, ascend);}template < typename T >
static void quickSort(T arr[], int len, bool ascend = true)
{quickSort(arr, 0, len-1, ascend);}

总结:

由于快速排序是判断Pivot基准值的排序,可能会将后者的某个相等的数放在最前面, 是个不稳定的算法.

快速排序比归并排序总结

由于归并排序涉及内存读写、所以快速排序比归并排序快、但是快速排序是个不稳定的算法、但是如果在局部大部分有序的情况下,还是使用归并排序快

下章学习:3.C++-对象排序比较_抵抗时间扭曲,坚持做一件有意义的事-CSDN博客

2.C++-选择排序、冒泡排序、插入排序、希尔排序、归并排序、快速排序相关推荐

  1. 直接插入排序 希尔排序 冒泡排序 快速排序 直接选择排序 堆排序 归并排序 基数排序的算法分析和具体实现 ...

    排序分为内部排序和外部排序 内部排序是把待排数据元素全部调入内存中进行的排序. 外部排序是因数量太大,把数据元素分批导入内存,排好序后再分批导出到磁盘和磁带外存介质上的排序方法. 比较排序算法优劣的标 ...

  2. 【Java】5大排序算法总结(插入排序+希尔排序+选择排序+堆排序+冒泡排序)

    快速导航: 1. 稳定性 2 . 插入排序 3. 希尔排序 4. 选择排序 5. 堆排序 6 冒泡排序 1. 稳定性 两个相等的数据,如果经过排序后,排序算法能保证其相对位置不发生变化,则我们称该算法 ...

  3. 【排序算法】冒泡排序|选择排序|插入排序|希尔排序

    文章目录 冒泡排序 选择排序 插入排序 希尔排序 冒泡排序   第一个元素开始向第二个元素比较,若大于则交换位置,不大于则不动.然后第二个元素和第三个元素比较,再然后第三个元素和第四个元素比较-一直比 ...

  4. 数据结构:直接插入排序 希尔排序 选择排序 堆排序 冒泡排序 快速排序 归并排序

    一.什么是排序 排序就是将一组杂乱无章的数据按照一定的次序组织起来,此次序可以是升序也可以是降序 二.为什么需要进行排序 为了满足一些需求,比如在比较学生的成绩时,我们就需要给所有学生的成绩排一个顺序 ...

  5. Java数据结构——直接插入排序+希尔排序+冒泡排序

    文章目录 一.插入排序 (一).什么是插入排序 (二).图例 (三).Java代码 二.希尔排序 (一).什么是希尔排序 (二).图例 (三).Java代码 三.冒泡排序 (一).什么是冒泡排序 (二 ...

  6. 插入排序:直接插入排序希尔排序

    一.直接插入排序 1. 思想 直接排序法, 可以分为两个部分, 一部分是有序的, 一部分是无序的. 从这个图上, 应该是能看清楚直接插入排序的思想了. 将无序部分的第一个与有序部分进行比较. 从有序部 ...

  7. 插入排序(折半查找优化插入排序||希尔排序) _清风明月

    插入排序:插入排序分为三个步骤: 1. 找位置.(序列可分为两个部分,第一个部分是有序序列,其二是非有序序列.当为有序序列时,查找可以改进为折半查找,优化算法速度.) 2. 移动. 3. 插入.查找的 ...

  8. 常见排序算法之希尔排序

    文章目录 1.概述 2.希尔排序之交换法 3.希尔排序之移动法 4.测试案例 1.概述 由于简单的插入排序每次数据量变多的时候,数据需要移动且交换数据的次数也会变多,继而影响效率.希尔排序就是在这个基 ...

  9. 我的Java开发学习之旅------gt;Java经典排序算法之希尔排序

    一.希尔排序(Shell Sort) 希尔排序(Shell Sort)是一种插入排序算法,因D.L.Shell于1959年提出而得名. Shell排序又称作缩小增量排序. 二.希尔排序的基本思想 希尔 ...

  10. 我的Java开发学习之旅------Java经典排序算法之希尔排序

    一.希尔排序(Shell Sort) 希尔排序(Shell Sort)是一种插入排序算法,因D.L.Shell于1959年提出而得名. Shell排序又称作缩小增量排序. 二.希尔排序的基本思想 希尔 ...

最新文章

  1. 概率论与数理统计--样本及抽样分布
  2. iPhone 隐私新规下的“大地震”:四大平台损失近百亿美元,“连用户是男是女都分不清……”
  3. 软件分发、补丁推送排错
  4. 手持gps坐标转换参数求解方法及在excel中的实现_分享∣Arcgis中62个常用技巧系列二(21-40技巧)...
  5. 用栈来求解汉诺塔变形问题
  6. Mysql 常用函数总结
  7. Kinect开发学习笔记之(七)骨骼数据的提取
  8. QPainter的使用
  9. VS中的lib和dll的区别和使用
  10. [Xcode 实际操作]一、博主领进门-(15)读取当前应用的信息
  11. 剥开比原看代码03:比原是如何监听p2p端口的
  12. 模板题——图论相关(1)
  13. python集合常用方法_python基础-集合set的常用方法
  14. 自然语言处理——第一章 绪论
  15. 在多媒体计算机中静态媒体是指,《多媒体技术及应用》按章复习题
  16. python 修改ubuntu 的ip
  17. 计算机在医学影像学的应用,计算机图像数字化与医学影像学之应用探析
  18. Apollo 6.0 Mid2Mid Planner论文阅读
  19. VS错误代码列表之美
  20. Scrolling and zooming chart with ChartScroller

热门文章

  1. 【游戏开发实战】Unity Unlit ShaderGraph实现与PBR的自发光贴图类似的叠加效果
  2. 天童美语植树节:前人栽树后人乘凉
  3. 怎么把html5做成动态壁纸,在电脑上怎样把小视频做成动态壁纸
  4. 双屏下微信截屏错位解决办法
  5. android manifest相关属性
  6. 计算机文本使用技巧,技巧:“文本恢复转换器”随附WORD的使用_计算机_IT /计算机_数据的基本知识...
  7. c++手机编程软件_今天小编推荐几款好用的手机编程APP!
  8. Ext_多行文本输入框_Ext.form.TextArea
  9. B站向快视频发律师函;2017年网络直播用户4.22亿;委内瑞拉预售“石油币”丨价值早报
  10. 999系统可用性时间表