数据结构中最全的8种排序算法总结

  • 1.插入排序
    • 代码如下:
  • 2.希尔排序
    • 代码如下:
  • 3.选择排序
    • 代码如下:
  • 4.堆排序
    • 代码如下:
  • 5.冒泡排序
    • 代码如下:
  • 6.快速排序
    • 代码如下:
  • 7.归并排序
    • 代码如下:
  • 8.计数排序
  • 9.小结
    • 对于数据结构中的排序算法,基本上都涉及到了,希望能给大家带来一些帮助,欢迎各位的留言评论,以及指点。觉得有用的可以收藏哦。

常见的排序算法有
插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序,下面一一介绍:

1.插入排序

插入排序的基本思想是:每一步将一个待排序元素按其关键字值得大小插入到已排序序列得适当位置上,直到待排序元素插入完为止。如果要对具有n个元素得数组arr进行排序,初始状态时,可以认为已排序序列为arr[0],待排序序列为arr[1]~arr[n-1],从arr[i]开始向arr[0]方向扫描各元素,寻找适当位置插入arr[i],依次进行,即可完成排序。

代码如下:

void insertSort(int* arr,int n)
{//假设第一个数据有序//未插入的数据:(1,n)for (int i = 1; i < n; ++i){//从有序数据的最后一个位置向前遍历int end = i - 1;int data = arr[i];while (end >= 0 && arr[end] >= data){//大的数据向后移动arr[end + 1] = arr[end];--end;}arr[end + 1] = data;}
}

插入排序总结:
1.元素集合越接近有序,直接插入排序算法的时间效率越高。
2.时间复杂度:O(N^2)
3.空间复杂度:O(1)
4.稳定性:稳定

2.希尔排序

希尔排序法的基本思想是:先选定一个整数gap值,把待排序文件中所有记录分成个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后重复上述分组和排序的工作。当gap=1时,所有记录在统一组内排好序。

代码如下:

void shellSort(int* arr, int n)
{int gap = n;while (gap > 1)//当gap等于1的时候变为插入排序{gap = gap / 3 + 1;//一趟希尔排序for (int i = gap; i < n; ++i){int end = i - gap;//同一组数据,最后一个有序数据的位置int data = arr[i];//待插入的数据while (end >= 0 && arr[end]>data){arr[end + gap] = arr[end];end -= gap;}arr[end + gap] = data;}}
}

希尔排序总结

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。
  3. 希尔排序的时间复杂度不好计算,需要进行推导,平均时间复杂度:O(N1.3—N2)
  4. 稳定性:不稳定

3.选择排序

选择排序思想是:每次从待排序序列中选择一个关键字最小的元素(当需要按关键字升序排列时),顺序排在已排序序列的最后,直至全部排完。而选择排序方法中最简的就是直接选择排序,就是通过顺序比较找出待排序序列中的最小元素。

代码如下:

void selectSort(int* arr, int n)
{int start = 0;//从未排序的序列中找最值,存放到未排序的起始位置int end = n - 1;//未排序的区间[start,end]while (start < end){int minIdx = start;//找到最小值得位置for (int i = start + 1; i <= end; ++i){if (arr[i] < arr[minIdx])minIdx=i;}Swap(arr,start,minIdx);//把最小值存开始的位置++start;//剩余的未排序的区间[start+1,end]}
}

选择排序总结:

  1. 选择排序好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

4.堆排序

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

代码如下:

//向下调整
void shiftDown(int* arr, int n, int parent)
{int child = 2 * parent + 1;while (child < n){if (child + 1 < n&&arr[child + 1] > arr[child])++child;if (arr[child]>arr[parent]){Swap(arr, child, parent);parent = child;child = 2 * parent + 1;}elsebreak;}
}
//堆排序
void heapSort(int* arr, int n)
{//建堆的过程(向下调整):建大堆for (int i = (n - 2) / 2; i >= 0; --i){shiftDown(arr,n,i);}int end = n - 1;while (end > 0){Swap(arr, end, 0);shiftDown(arr,end,0);--end;}
}

堆排序总结:

  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

5.冒泡排序

基本思想是:(1)对具有n个元素的序列按升序进行冒泡排序,首先将第一个元素与第二个元素进行比较,若为逆序,则将两元素交换。然后比较第二、第三个元素,以此类推,直到第n-1和第n个元素进行比较和交换。此过程为第一趟起泡排序。经过第一趟最大的元素便被交换到了第n的位置。
(2)对前n-1个元素进行第二趟起泡排序,将其中最大元素交换到第n-1个位置。
(3)如此继续,直到某一趟排序未发生任何交换时,排序完毕。对n个元素的序列,起泡排序最多需要n-1趟。在这个排序过程中小的气泡往上浮,大的气泡往下沉,就像冒泡一样,因此称为冒泡排序。

代码如下:

void bubbleSort(int* arr, int n)
{int end = n;//每一次遍历范围:[0,未排序数据的最后一个位置]while (end>1){int flag = 0;//标记一轮冒泡排序中是否发生了交换操作//一轮冒泡排序for (int i = 1; i < end; ++i){if (arr[i - 1]>arr[i]){Swap(arr,i-1,i);//大的向后移动flag = 1;}}if (!flag)//说明剩余元素全部有序break;--end;}
}

冒泡排序总结

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

6.快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
将区间按照基准值划分为左右两半部分的常见方式有:
1. hoare版本
2. 挖坑法
3. 前后指针版本

基准值获取方法:三数去中法 :即起始,中间,结束。

代码如下:

int getMid(int* arr, int begin, int end)
{int mid = begin + (end - begin) / 2;if (arr[begin] > arr[end]){if (arr[mid] > arr[end])return mid;else if (arr[begin] > arr[end])return end;elsereturn begin;}else{if (arr[mid] < arr[end])return mid;else if (arr[begin] < arr[end])return end;elsereturn begin;}
}

第一种基准值划分的方式:hoare版本,代码如下:

int partion1(int* arr, int begin, int end)
{int mid = getMid(arr,begin,end);//获取基准值的位置Swap(arr,begin,mid);int key = arr[begin];//基准值的数据int start = begin;while (begin < end){//从后向前找小于基准值的位置while (begin<end && arr[end] >= key)--end;//从前向后找大于基准值的位置while (begin<end && arr[begin] <= key)++begin;Swap(arr, begin, end);}//相遇位置的数据和基准值数据交换Swap(arr,start,begin);return begin;
}

第二种基准值划分的方式:挖坑法,代码如下:

int partion2(int* arr, int begin, int end)
{//第一个值作为基准值,第一个位置为初始的坑的位置int mid = getMid(arr,begin,end);Swap(arr,begin,end);int key = arr[begin];while (begin < end){//从后向前找小的while (begin < end&&arr[end] >= key)--end;//填坑arr[begin] = arr[end];//从前向后找大的while (begin < end&&arr[begin] <= key)++begin;//填坑arr[end] = arr[begin];}arr[begin] = key;return begin;
}

第三种基准值划分的方式:前后指针版本,代码如下:

int partion3(int* arr, int begin, int end)
{int mid = getMid(arr, begin, end);Swap(arr, begin, end);//上一个小于基准值的位置int prev = begin;//下一个小于基准值的位置int cur = begin + 1;int key = arr[begin];while (cur <= end){//当cur走到下一个小于基准值的位置,判断prev和cur是否连续if (arr[cur] < key&& ++prev != cur){//不连续则交换数据:prev和cur数据交换Swap(arr,prev,cur);}++cur;}//cur走到结尾,则交换pre和key的数据,最后将数据划分为两部分Swap(arr,begin,prev);return prev;
}

例如:以递归法用hoare法划分基准值的方式的快速排序

void quickSort(int* arr, int begin, int end)
{if (begin >= end)return;//div是一次划分之后,基准值位置//partion1函数是hoare法,可改为partion2挖坑法,partion3前后指针法int div = partion1(arr,begin,end);//基准值左右两部分进行快速排序//[begin,div-1]quickSort(arr,begin,div-1);//[div+1,end]quickSort(arr, div + 1, end);
}

快速排序总结:

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

7.归并排序

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

代码如下:

//归并排序递归方式
void merge(int* arr, int begin, int mid, int end, int* tmp)
{//子区间:[begin,mid]  [mid+1,end]int begin1 = begin;int end1 = mid;int begin2 = mid + 1;int end2 = end;//辅助空间的起始位置int idx = begin;//合并有序序列while (begin1 <= end1&&begin2 <= end2){if (arr[begin1] <= arr[begin2])tmp[idx++] = arr[begin1++];elsetmp[idx++] = arr[begin2++];}//判断是否有未合并的元素if (begin1 <= end1)memcpy(tmp+idx,arr+begin1,sizeof(int)*(end1-begin1+1));if (begin2 <= end2)memcpy(tmp+idx,arr+begin2,sizeof(int)*(end2-begin2+1));//合并之后的序列考到原始数组的对应区间memcpy(arr+begin,tmp+begin,sizeof(int)*(end-begin+1));}
void _mergeSort(int* arr, int begin, int end, int* tmp)
{if (begin >= end)return;int mid = begin + (end-begin) / 2;//首先分解成子序列_mergeSort(arr,begin,mid,tmp);_mergeSort(arr,mid+1,end,tmp);//合并两个有序的子序列[begin,mid]  [mid+1,end]merge(arr,begin,mid,end,tmp);
}void mergeSort(int* arr, int n)
{int* tmp = (int*)malloc(sizeof(int)*n);_mergeSort(arr,0,n-1,tmp);free(tmp);
}

归并排序非递归法:

void mergeSortNoR(int* arr, int n)
{int* tmp = (int*)malloc(sizeof(int)*n);//子序列的步长int step = 1;while (step < n){for (int idx = 0; idx < n; idx += 2 * step){//找到两个待合并的子序列区间int begin = idx;int mid = idx + step - 1;//判断是否存在第二个子序列if (mid >= n - 1)continue;int end = idx + 2 * step - 1;//判断第二个子序列是否越界if (end >= n)end = n - 1;merge(arr, begin, mid, end, tmp);}//更新步长step *= 2;}
}

归并排序总结:

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定

8.计数排序

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。 操作步骤:1. 统计相同元素出现次数。2. 根据统计的结果将序列回收到原来的序列中。

void countSort(int* arr, int n)
{//找到最大和最小值int max, min;min = max = arr[0];for (int i = 1; i < n; ++i){if (arr[i]>max)max = arr[i];if (arr[i] < min)min = arr[i];}//计算范围int range = max - min + 1;//创建一个计数数组,初始化为0int* countArr = (int*)calloc(range,sizeof(int));//计数for (int i = 0; i < n; ++i){countArr[arr[i] - min]++;}//遍历计数数组,int idx = 0;for (int i = 0; i < range; ++i){while (countArr[i]--){arr[idx++] = i + min;}}
}

计数排序总结:

  1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
  2. 时间复杂度:O(MAX(N,范围))
  3. 空间复杂度:O(范围)
  4. 稳定性:稳定

9.小结

对于数据结构中的排序算法,基本上都涉及到了,希望能给大家带来一些帮助,欢迎各位的留言评论,以及指点。觉得有用的可以收藏哦。

<<数据结构中最全的8种排序算法总结>>相关推荐

  1. ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  2. ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  3. 信息学奥赛真题解析(玩具谜题)

    玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...

  4. 信息学奥赛之初赛 第1轮 讲解(01-08课)

    信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...

  5. 信息学奥赛一本通习题答案(五)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  6. 信息学奥赛一本通习题答案(三)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  7. 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题

    第1章   快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章  素数 第 3 章  约数 第 4 章  同余问题 第 5 章  矩阵乘法 第 6 章 ...

  8. 信息学奥赛一本通题目代码(非题库)

    为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...

  9. 信息学奥赛一本通(C++版) 刷题 记录

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...

  10. 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离

    首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...

最新文章

  1. springmvc中的类型转换器
  2. Mysql 索引 总结 —— 概述 || 索引优势劣势|| 索引结构(索引是在MySQL的存储引擎层中实现的)|| BTREE 结构||B+TREE 结构||MySQL中的B+Tree||索引分类
  3. 无法创建 set/get 参数(参数 ID)
  4. vue 实现无限轮播_用vue写一个轮播图效果
  5. 处理字符串_11_判断一个字符是否是数字
  6. php5.3.*编译出现make: *** [ext/gd/libgd/gd_compat.lo] Error 1 解决方法
  7. HTTP响应报文与工作原理详解
  8. JMetro版本4.8已发布
  9. Mounty for NTFS免费软件实现MAC OS X下对NTFS分区进行读写
  10. 基于 SpringBoot2.0+优雅整合 SpringBoot+Mybatis
  11. [译] 美国证券法对 ICO 及相关 Fund 的最新动态
  12. 勉强算是面经——1.诺瓦科技
  13. 格雷希尔GripSeal螺纹接头的规格型号
  14. Modelica运算符
  15. 递归的本质(栈:后进先出)
  16. Mybatis中使用左连接查询
  17. MySQL Error 1114
  18. 我与 SAP 成都研究院吴院长的二三事
  19. 究竟哪些语句是属于DDL?
  20. 百度Sugar BI 数据可视化里的标签页组件如何实现

热门文章

  1. tf.keras softmax多分类
  2. python h_pythonh控制
  3. 七段数码管数字字母显示
  4. can总线程序讲解_详解stm32的CAN控制器(程序分享)
  5. iptables企业面试题集锦
  6. aix系统计算时间差_室内人员定位系统
  7. C#编程中的数据库的连接字符串
  8. 李岳恒:2019,媒体的大变局
  9. token 过期实例
  10. 即时通讯源码,社交软件开发