莫名其妙的算法们:排序(一)
来源:互联网 发布:淘宝卖点文案 编辑:程序博客网 时间:2024/06/06 15:04
排序可是说是算法里面的经典问题了。排序算法的发展史,可以看作是算法进化史的一个缩影。
启蒙
在这一阶段的算法明确地提出了排序问题,并给出了简单易行的解决方案。虽然局限于奢侈的时间复杂度,但为后序的发展给出了良好的开端。
冒泡排序
冒泡排序几乎是每个初学者最早接触到的排序方案。其大体思想是每次遍历时,逐一比较相邻的两元素,将较大者交换至右侧。这样每次遍历都可以将当前序列的最大值交换至序列的最右端。不断进行以上的遍历方案,并在该过程中缩小待处理序列的规模,直至整个过程完成。
int* bubbleSort(int* A, int n) { //长度为n的数组需要遍历n-1次 for(int i=0;i<n-1;++i){ //遍历开始时右侧已排好i个元素,故待处理区间为[0,n-i) //因为需要j+1<n-i所以j的取值是[0,n-i-1) for(int j=0;j<n-i-1;++j){ if(A[j]>A[j+1]){ swap(A[j],A[j+1]); } } } return A;}
一个简单的冒泡排序示意图:
选择排序
选择排序更容易理解一些。其大体思想是每次从待处理序列中选出最小者交换至最左端,然后对剩下的序列继续该操作直至排序结束。
int* selectionSort(int* A, int n) { //长度为n的数组需要遍历n-1次 for(int i=0;i<n-1;++i){ //每次找出最小值的下标 int min=i; //A[i]暂定为最小值,故j遍历范围是[i+1,n) for(int j=i+1;j<n;++j){ if(A[j]<A[min]) min=j; } //交换 swap(A[min],A[i]); } return A;}
插入排序
插入排序很类似与打扑克牌时,把一张新摸到的牌插入到手牌里的处理方式。其大体思想是每次处理一个元素,该元素的左侧是已经处理好的有序数列(初始长度为1),将该元素插入到此数列的正确位置中去。
int* insertionSort(int* A, int n) { //初始时认为A[0]是有序数列,待处理元素范围是[1,n) for(int i=1;i<n;i++){ int get=A[i]; //j视作是待处理元素应插入数列中的位置,从i-1到0逆序搜索 int j=i-1; //搜索条件:j未越界且位置j上的元素大于待处理元素 while(j>=0 && A[j]>get){ //相当于元素右移一位 A[j+1]=A[j]; j--; } //循环结束时,若j越界则需插入到A[0]处; //否则A[j]<=get需要插入到A[j+1]处 A[j+1]=get; } return A;}
其他
除了上述之外还有一些改良算法,比如对冒泡排序改良的鸡尾酒排序,不过并未降低时间复杂度;对选择排序的双向选择排序,有轻微的时间复杂度改良;对插入排序的二分法插入排序,可以节省比较次数,但避免不了遍历次数。
总结与对比
时间复杂度:O(N2)
共需要N-1次循环,每次需要遍历N-…
成熟
成熟时期的排序算法可以达到O(NlogN)的时间复杂度,基本上已经是排序的极限了。尽管后序有O(N)的方案,但也仅仅在特定条件下有效。
快速排序
快速排序的思路不难想到,找出一个基准元素,小于它的放数组左边,大于它的放右边,然后对左右的子数组分别进行如上操作。其核心手段在于对数组进行调节,使得小于和大于基准元素的数据分布于它的异侧。
这个问题早期有这样一种解法:指针i和指针j分别指向首尾,并逐步向中间靠拢,指针i找到一个大于基准的元素,等待j指针找到一个小于基准的元素,两者交换。
不过现在比较流行的解法是下面这样的:给定一个小于等于区间(初始右边界为-1);当发现一个小于基准的元素,就把它与小于等于区间的右侧元素交换,同时该区间扩充一位。
归并排序
将两个有序数组合并是比较容易的,那么
- 莫名其妙的算法们:排序(一)
- Web页面莫名其妙的问题【原因一:兼容模式】
- 《排序算法一》:容易理解的 冒泡
- 一亿个数排序的算法
- 稳定的排序算法(一)
- 不稳定的排序算法(一)
- 常见的JAVA排序算法(一)
- 有意思的算法(一)----冒泡排序
- 一、常见的几种排序算法
- 常用的排序算法(一)
- 关于排序算法的理解(一)
- 简单的排序算法整理(一)
- 排序算法的总结(一)
- JavaScript常见的排序算法(一)
- 排序算法的数组实现 -- 插入排序(一)
- 神的规范:排序算法(一):简单插入排序
- 常见的排序算法(一) 插入排序
- 常被提到的排序算法一(冒泡排序)
- RxJava 1.x from()和just()用法、区别和举例
- 页面loading功能的实现
- 微信开发-微信支付-回调(notify_url)失败-解决思路
- 静态变量和动态变量区别
- LINQ系列:C#中与LINQ相关特性
- 莫名其妙的算法们:排序(一)
- c++ code: 最长回文串
- 通过Java.awt.Robot来控制鼠标键盘自动化
- 欢迎使用CSDN-markdown编辑器
- 51Nod-1463-找朋友
- MySQL---数据库从入门走向大神系列(十四)-ComboPooledDataSource(C3P0连接池配置)
- Python中浮点数精度处理
- 十三、适配器模式——设计模式学习笔记
- [LeetCode 解题报告]010.Regular Expression Matching