目录

常见的八种排序

直接插入排序

希尔排序

直接选择排序

堆排序

冒泡排序

快速排序

hoare版本

挖坑法

前后指针版

快速排序代码

归并排序

计数排序


常见的八种排序

直接插入排序

⾸先,我们将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有⼀个元素,就是数组的第⼀个元素。插⼊算法的核⼼思想是取未排序区间中的元素,在已排序区间中找到合适的插⼊位置将其插⼊,并保证已排序区间数据⼀直有序。重复这个过程,直到未排序区间中元素为空,算法结束

如图所示,要排序的数据是4,5,6,1,3,2,其中左侧为已排序区间,右侧是未排序区间。

插⼊排序包含两种操作,⼀种是元素的⽐较,⼀种是元素的移动。比如我们需要将⼀个数据3插⼊
到已排序区间[1,4,5,6]时,需要拿3与已排序区间的元素6,5,4,1依次⽐较⼤⼩,找到合适的插⼊位置。找到插⼊点之后,我们还需要将插⼊点之后的元素顺序往后移动⼀位,这样才能腾出位置给元素3插⼊。比如3和6比较,此时就可以将3和6的位置进行交换,依次类推,3再和5交换,3再和4交换,当3和1比较发现3比1大就不用再交换了,完成了3的插入过程了

C代码

输出结果

时间复杂度O(N^2),空间复杂度O(1)

稳定性:稳定

稳定性的说明

图中红色的5在排完序后依旧在蓝色的5后面,这就是稳定的表现

希尔排序

希尔排序可以看成是对直接插入排序的优化:我们可以看到直接插入排序的缺点即对一个降序的数组进行升序那么时间复杂度为O(N^2),但是对该数组进行降序那么时间复杂度就为O(N)了,此时大家会想该数组都是降序的了,你再对其降序图啥呢?这里想阐述的观点是如果将该数组变为

接近有序的状态那么使用直接插入排序其时间复杂度不就降下来了吗,希尔排序就是用了这个思想

对直接插入排序进行了优化!!!

执行结果

时间复杂度O(N^logN),空间复杂度O(1)

稳定性:不稳定

直接选择排序

基本思想:一次挑一个数据:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完

排序结果:

堆排序

注意:使用堆排序首先需要理解什么是堆,大堆与小堆的区别,这里就不对堆的概念进行说明

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

将数组key=[20,17,4,16,5,3]建立一个大堆,对该数组排升序

执行结果:

上面代码中数组arr已经是个大堆了,这里只是对堆排序的过程进行了说明,

如何将一个普通的数组建立成一个大堆这里就不作讲述

  1. 时间复杂度:O(N*logN)
  2. 空间复杂度:O(1)
  3. 稳定性:不稳定

冒泡排序

冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进⾏⽐较,看是否满⾜⼤⼩关系要求。如果不满⾜就让它俩互换。图中相邻的元素如果左边的元素大于右边的元素,那么就进行交换,即相邻的两个元素右边总是较大的。

  1. 时间复杂度:O(N^2)
  2. 空间复杂度:O(1)
  3. 稳定性:稳定

快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后对左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

将区间按照基准值划分为左右两半部分的常见方式有:

  1. hoare版本
  2. 挖坑法
  3. 前后指针版本

  1. 三数取中法选key(可以保证不会出现最坏的情况,而且当数据有序的时候就是最好的情况)
  2. 递归到小的子区间时,可以考虑使用插入排序
//快排,时间复杂度,最好的情况O(N*log2(N)),最坏O(N^2)
//优化方法1:三数取中,避免快排出现最坏的情况
int GetMidIndex(int* a, int left, int right)
{int mid = (left + right) >> 1;//移位的效率比除以2的效率要高一点// left  mid  rightif (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}else // a[left] >= a[mid]{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]){return left;}else{return right;}}
}

hoare版本

int PartSort1(int* a, int left, int right)//排序一趟就返回相遇点
{int midIndex = GetMidIndex(a, left, right);//使用三数取中Swap(&a[left], &a[midIndex]);//将三数取中后的结果与最左边的值进行交换int keyi = left;while (left < right){// 找小while (left < right && a[right] >= a[keyi])--right;// 找大while (left < right && a[left] <= a[keyi])++left;Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);return left;//返回相遇的位置,也就是keyi
}

挖坑法

int PartSort2(int* a, int left, int right)
{int midIndex = GetMidIndex(a, left, right);//使用三数取中Swap(&a[left], &a[midIndex]);//将三数取中后的结果与最左边的值进行交换int key = a[left];//将最左边的值给key,然后将最左边的视为坑(没有数据的意思)while (left < right){// 找小while (left < right && a[right] >= key){--right;}// 放到左边的坑位中,右边就形成新的坑a[left] = a[right];// 找大while (left < right && a[left] <= key){++left;}// 放到右边的坑位中,左边就形成新的坑a[right] = a[left];}a[left] = key;//最后相遇点一定是坑,将key放到坑中return left;//返回相遇点,也就是key值所在的位置
}

前后指针版

int PartSort3(int* a, int left, int right)
{int midIndex = GetMidIndex(a, left, right);Swap(&a[left], &a[midIndex]);int keyi = left;int prev = left, cur = left + 1;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur)//cur找比keyi小的数{Swap(&a[cur], &a[prev]);}++cur;}Swap(&a[keyi], &a[prev]);return prev;//最后返回prev的位置,也就是keyi,这里的keyi表示的是下标
}

快速排序代码

上面的都是各版本进行单趟排序的代码

void QuickSort(int* a, int begin, int end)//(用的是hoare法)
{if (begin >= end)//[begin,end]区间为0或者区间不存在则返回return;// 1、如果这个子区间是数据较多,继续选key单趟,分割子区间分治递归// 2、如果这个子区间是数据较小,再去分治递归不太划算//此时越往后递归,子区间就越多,每个子区间的数据就越少,每个子区间都要递归就不划算,//可以在后面进行插入排序,因为此时每个子区间是接近有序的,接近于希尔排序了if (end - begin > 0)//小区间优化的效果没那么明显,如果对相应数据量级进行针对性的调//往往数据量越大,比如将20换成1000效果就明显了,20是官方给的,官方不敢给大了{int keyi = PartSort1(a, begin, end);//int keyi = PartSort2(a, begin, end);//int keyi = PartSort3(a, begin, end);// [begin, keyi-1] keyi [keyi+1, end]QuickSort(a, begin, keyi - 1);//递归QuickSort(a, keyi + 1, end);//递归}else{InsertSort(a + begin, end - begin + 1);//HeapSort(a + begin, end - begin + 1);也可以换成其如堆排,希尔排序效果会更好}
}
  1. 时间复杂度:O(N*logN)
  2. 空间复杂度:O(logN)
  3. 稳定性:不稳定

归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

void _MergeSort(int* a, int left, int right, int* tmp)
{if (left >= right)return;int mid = (left + right) >> 1;// [left, mid][mid+1,right]_MergeSort(a, left, mid, tmp);//先递归进行分治_MergeSort(a, mid + 1, right, tmp);// 两段有序子区间归并tmp,并拷贝回去int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int i = left;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2])tmp[i++] = a[begin1++];elsetmp[i++] = a[begin2++];}while (begin1 <= end1)tmp[i++] = a[begin1++];while (begin2 <= end2)tmp[i++] = a[begin2++];// 归并完成以后,拷贝回到原数组for (int j = left; j <= right; ++j)a[j] = tmp[j];
}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int)*n);//创建临时数组if (tmp == NULL){printf("malloc fail\n");exit(-1);}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}
  1. 时间复杂度:O(N*logN)
  2. 空间复杂度:O(N)
  3. 稳定性:稳定

计数排序

//计数排序
void CountSort(int* a, int n)
{int min = a[0];//记录数组中的最小值int max = a[0];//记录数组中的最大值for (int i = 0; i < n; i++){if (a[i] < min)min = a[i];if (a[i] > max)max = a[i];}int range = max - min + 1;//min和max之间的自然数个数(包括min和max本身)int* count = (int*)calloc(range, sizeof(int));//开辟可储存range个整型的内存空间,并将内存空间置0if (count == NULL){printf("malloc fail\n");exit(-1);}//统计相同元素出现次数(相对映射)for (int i = 0; i < n; i++){count[a[i] - min]++;}int i = 0;//根据统计结果将序列回收到原来的序列中for (int j = 0; j < range; j++){while (count[j]--){a[i++] = j + min;}}free(count);//释放空间
}
  1. 时间复杂度:O(MAX(N+范围))
  2. 空间复杂度:O(范围)
  3. 稳定性:稳定

八种常见排序算法细讲相关推荐

  1. 八种经典排序算法总结

    前言 算法和数据结构是一个程序员的内功,所以经常在一些笔试中都会要求手写一些简单的排序算法,以此考验面试者的编程水平.下面我就简单介绍八种常见的排序算法,一起学习一下. 一.冒泡排序 思路: 比较相邻 ...

  2. 排序 八种经典排序算法

    排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列. 我整理了以前自己所写的一些排序算法结合网上的一些资料,共介绍8 ...

  3. 剖析八种经典排序算法

    排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列. 我整理了以前自己所写的一些排序算法结合网上的一些资料,共介绍8 ...

  4. 八种常用排序算法参考

    写在前面: 因为网络上有很多篇讲排序算法的,所以这里就不详细讲了,只作为参考和自己查阅 当然了,虽然篇幅也会短很多,但部分重点和要点还在 八种排序算法分别是: ①选择排序: ②冒泡排序: ③插入排序: ...

  5. 八种常见排序方法总结 C/C++代码实现

    目前评价排序算法的好坏标准主要有两点: 1.执行时间:高效的排序算法的比较次数和移动次数都应该尽可能的少. 2.辅助空间:算法执行期间所需要的辅助空间与待排序数据量无关. 文章目录 1.冒泡排序 2. ...

  6. Java几种常见排序算法与代码实现

    前言: 排序算法也算是每年校招.春招.社招都会问到的问题,虽然每次复习了就忘,但是也可以隔一段时间又拿出来看看. 其中,排序方式指,内部还是外部排序.只需要内部内存就可以的称为内部排序,数据量太大需要 ...

  7. php 各种排序算法,PHP四种常见排序算法

    一.冒泡排序: 冒泡排序可以说是最常见,也是最简单,最经典的排序算法了. 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换 ...

  8. python 实现数据结构八种内部排序算法

    目录 一.插入排序 二.希尔排序 三.冒泡排序 四.快速排序 五.选择排序 六.堆排序 七.归并排序 八.基数排序 一.插入排序 array_test = [8,3,5,1,10,4,2,6,7,9] ...

  9. 利用Python实现几种常见排序算法

    一.排序算法概览 插入排序:直接插入排序,二分法插入排序 选择排序:直接选择排序,堆排序 交换排序:冒泡排序,快速排序 归并排序 二.代码实现 1.直接插入排序 最简单直接的一种方式,序列在排序中可分 ...

最新文章

  1. python 编程笔记
  2. Python小游戏(小蜜蜂)
  3. SAP Spartacus如何为不同的environment设置不同的baseUrl
  4. pyqt5 点击开始执行_《快速掌握PyQt5》第一章 PyQt5的起点
  5. Java 14 来了!
  6. python中confIgparser模块学习
  7. PHP 统计一个字符串,在另一个字符串中出现次数
  8. 2020年微信视频号数据分析生态趋势调查报告
  9. Cinema 4D Mac(C4D)常用快捷键与自定义快捷键
  10. 高仿iOS微信客户端
  11. Java伪随机数生成器(PRNG)中的弱点
  12. SQL 2008函数大全
  13. 你需要知道的 TCP 三次握手
  14. cannot build player while editor is importing assets or compiling scripts
  15. Python中的四种队列(queue)、堆(heap)
  16. 路飞学城1之课程与课程详细
  17. 模拟CMOS集成电路设计入门学习(1)
  18. 2013年火车票之抢票神器--【车票无忧】
  19. 1225-上期所结算延迟+我脑子坏了,写作业不看要求,多写了一个哈哈哈。
  20. 借助PLC-Recorder,西门子PLC S7-1200实现4ms准确周期采集的方法(带时间戳采集)

热门文章

  1. React,手写简易redux(二)- By Viga
  2. Android工具修复属性,Broken Android Data Extraction(安卓数据修复工具) V3.0.20 官方版
  3. oracle00279,ORACLE數據恢復
  4. 如何一键计算OR值?
  5. MATLAB中datetime函数的使用
  6. Python每日bug定时发送飞书群
  7. 机械革命无法使用U盘启动linux,机械革命u盘启动,详细教您机械革命bios怎么设置u盘启动...
  8. 创新实训(2)-Scrapy 学习
  9. Redis GEO详解
  10. Android中按钮的点击事件的四种写法