(数据结构)七种常用的排序算法分析及代码实现(上)
来源:互联网 发布:game ec 源码 编辑:程序博客网 时间:2024/06/05 20:47
直接插入排序
思路分析
- 1、首先,我们需要从前往后遍历数组。
- 2、其次,在遍历数组的同时,我们需要将当时所遍历到的数字(存储在tmp的临时变量中,防止因循环的原因而丢失数据)与之前已排序好的部分进行从后往前的比较。
- 3、当比之前的数字更小时,我们需要进行重新排序,即将end处数字放到end+1处,然后将tmp插入至end处。之后,再与前面的数字进行比较,即end- -。
- 4、当比之前的数字更大时,则直接将数字插入到end+1处即可。然后进行下一个数字的遍历。
- 5、易知,我们需要两层循环嵌套,来实现我们的插入排序。
代码实现
//直接插入排序void StraightInsertion(int a[], size_t n){ for (size_t i = 1; i < n; ++i) { int tmp = a[i]; for (size_t end = i - 1; end >= 0; --end) { if (tmp < a[end]) { a[end + 1] = a[end]; a[end] = tmp; } else { a[end + 1] = tmp; break; } } }}
时间复杂度
- 当插入的数组已是有序时,则算法的最佳时间复杂度为O(N)
- 当插入的数据是逆序时(针对上述升序排序算法),则有最坏时间复杂度O(N^2)
希尔排序
显而易见的是,直接插入排序算法在数据量较少的时候,效率还不错。但如果涉及到较多数据的排序,时间复杂度则会出现显著的提升。
希尔排序是基于直接插入排序算法的一个优化算法,即在对数组进行排序前,先通过一定操作使数组基本有序。从而在进行后续的直接插入排序操作时,达到降低时间复杂度的目的。
思路分析
- 1、与直接插入算法相比,我们的主要策略是需要先使数组基本有序。
2、这里采取跳跃分割策略,将相距某个增量(gap)的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序,而不是局部有序的。
3、我们对gap值进行递减循环,即对数组进行多次的子序列排序,这样我们得到的数组就会是一个大体有序的数组。之后再进行直接插入排序的话,可以大大降低排序的时间复杂度。
代码实现
void ShellSort(int* a, size_t n){ assert(a); int gap = n; while (gap > 1) { gap = gap / 3 + 1; for (size_t i = 0; i < n - gap; ++i) { int end = i; int tmp = a[end + gap]; while (end >= 0) { if (tmp < a[end]) { a[end + gap] = a[end]; end -= gap; } else break; } a[end + gap] = tmp; } }}
时间复杂度
- 当插入的数组已是有序时,则算法的最佳时间复杂度为O(N)
- 当插入的数据是逆序时(针对上述升序排序算法),则有最坏时间复杂度O(N^2)
- 平均时间复杂度为O(N^1.3)
选择排序
选择排序是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。
思路分析
- 我们拿下标来控制我们的数组,以区间法进行控制
- min作为哨兵来控制
- 显然,我们需要用两层循环嵌套来实现
代码实现
//选择排序void SelectionSort(int a[], size_t n){ for (size_t i = 0; i < n; ++i) { int min = i; //找到当前数组内的最小值 for (size_t j = i + 1; j < n; ++j) { if (a[min]>a[j]) min = j; } //进行交换 if (i != min) { int temp = a[i]; a[i] = a[min]; a[min] = temp; } }}
时间复杂度
- 因为两层嵌套的存在,故而时间复杂度稳定在O(N^2)。
堆排序
堆排序就是利用堆来进行排序的方法。
假设我们利用大顶堆来进行排序的实现。大顶堆,显而易见的就是将整个序列内的最大值放在堆顶。将它移走(和最后一个元素进行交换),然后将剩余的n-1个序列重新构造成一个堆,即得到n-1个数中次大的值。
反复迭代执行,便能得到一个有序序列了。
思路分析
- 1、构建大顶堆
- 2、利用两次循环,不断进行堆调整和数据间交换。
- 3、交换数据,然后重新构建堆
代码实现
//向下调整算法void _AdjustDown(int* a, int i, int n){ int parent = i; int child = parent * 2 + 1; int temp = a[parent]; while (child < n) { if (child + 1 < n &&a[child + 1] > a[child]) ++child; if (a[child] < a[parent]) break; a[parent] = a[child]; parent = child; child = parent * 2 + 1; } a[parent] = temp;}//堆排序void HeapSort(int* a, int n){ //构建大顶堆 for (int i = (n/2-1); i >= 0; --i)//n/2-1是第一个非叶子结点 { _AdjustDown(a,i,n); } for (size_t i = n-1; i>0; --i) { //交换最后一个节点和大顶堆的最大值 int temp = a[0]; a[0] = a[i]; a[i] = temp; //剩余的值继续调整 _AdjustDown(a, 0, i-1); }}
时间复杂度
- 构建堆的时间复杂度为O(N),取堆顶然后重建堆需要O(LogN),所以整体的时间复杂度为O(nLogN)
冒泡排序
冒泡排序是一种交换排序,其基本思想是:两两比较相邻记录的关键字,如果反序则交换,直至没有反序的记录为止。
思路分析
- 将每一个关键字与数列最后的关键词开始,从后往前比较。
- 逐一比较时,将较小值交换到前面,直到最后找到最小值放置在第一个位置。
- 用标记变量exchange,判断序列是否有序。若有序,则不再进行之后的循环工作。
代码实现
//冒泡排序void BubbleSort(int* a, size_t n){ assert(a); bool exchange = false; for (size_t i = 0; i < n; ++i) { for (size_t end = n-1; end > i ; --end) { if (a[i]>a[end]) { int temp = a[i]; a[i] = a[end]; a[end] = temp; } exchange = true; } if (exchange == false)//针对有序数组的优化 break; }}
时间复杂度
- 由两层循环嵌套易知,冒泡排序的时间复杂度为O(N^2)。
References
图中动图来源:http://dsqiu.iteye.com/blog/1706817
阅读全文
0 0
- (数据结构)七种常用的排序算法分析及代码实现(上)
- (数据结构)七种常用的排序算法分析及代码实现(下)——快速排序及归并排序
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 常用的算法和数据结构分析(查找和排序)
- 数据结构实验4(排序算法的实现及性能分析)
- 常用的排序算法及其Java实现(上)
- 常用排序算法及代码实现
- 经典排序算法的详细分析及C++实现代码
- 一步步学习数据结构和算法之常用排序效率分析及java实现
- 七种常见排序算法及实现
- 数据结构与算法分析11(排序-快速排序及相关分析、排序分析)
- 数据结构—各类‘排序算法’实现(上)
- 数据结构排序算法代码实现
- 常用排序算法的具体代码实现
- 常用排序算法的实现和分析
- 常用排序算法的分析与实现
- 高精度算法数据结构及常用函数实现(C++)
- 常用查找数据结构及算法(Python实现)
- 没有网照样激活软件,用亿图就是这么任性!
- 【java基础】02.Collection类集
- android权限大全
- String和StringBuffer的replace问题
- 任意两点之间的最短路径问题(Floyd算法)--Java语言
- (数据结构)七种常用的排序算法分析及代码实现(上)
- 要努力啊!
- appserv安装之后无法访问localhost
- Android之BroadcastReceiver总结
- 内存分配与回收策略
- java 多态的理解(2)
- Android中实现 滑动时将指定View定位在顶部
- c# windows 服务 定时器 每天凌晨0点整执行任务
- Robot Framework自定义库的加载路径和顺序