常见排序算法总结:插入排序,希尔排序,冒泡排序,快速排序,简单选择排序以及java实现
今天来总结一下常用的内部排序算法。内部排序算法们需要掌握的知识点大概有:算法的原理,算法的编码实现,算法的时空复杂度的计算和记忆,何时出现最差时间复杂度,以及是否稳定,何时不稳定。
首先来总结下常用内部排序算法:
| 类别 | 名称 | 时间复杂度(默认最坏情况) | 空间复杂度 | 稳定性 | 备注 |
| 插入排序 | 直接插入排序 | O(n^2) | O(1) | 稳定 | |
| 插入排序 | 希尔排序 | 最坏O(n^2),平均O(n^1.3) | O(1) | 不稳定 | |
| 交换排序 | 冒泡排序 | O(n^2) | O(1) | 稳定 | |
| 交换排序 | 快速排序 | 最坏O(n^2),平均O(nlogn) | 最坏O(n),平均O(logn) | 不稳定 | |
| 选择排序 | 简单选择排序 | O(n^2) | O(1) | 不稳定 | 简单方法中唯一一个不稳定 |
一、基础知识温习:
在进行具体算法的总结前,我们先来温习一下一些基本的概念和方法:
算法的稳定性:如果排序后,两个拥有相等关键字的元素a和b的相对位置没有发生变换,则稳定,否则不稳定。
内部排序是指在排序期间元素全部存放在内存中的排序;外部排序是指在排序期间元素无法全部同时存放在内存中,必须在排序过程中根据要求不断地在内、外存之间移动的操作。
然后再来温习一下时间复杂度的计算:
时间复杂度的计算其实就是计算出算法某条执行语句的具体执行频度,之后取频度的数量级即可。下面用例子温习:
例1:
void fun(int n){
;
;
}
设语句 i*=2运行t次,则,$t=max_t t, s.t. 2^t<n$,即t是使2^t<n成立最大的t。故$t=log_2 n(向下取整)$。故时间复杂度O(log2 n)。
例2:
count=; ;k<=n;k*=) ;j<=n;j++) count++;
外循环t次,2^t=n,t=log2 n。内循环n次。故O(nlog2n)。
例3:
y=; )(y+)<=n) y+=;
$t=max_t t , s.t. t^2<=n$t=n^(1/2)。O(n^0.5)。
例4:
;i<=n;i++)
;j<=i;j++)
;k<=j;k++)
x++;
内层循环受外循环控制变量大小控制,故不能简单相乘。研究发现,i=某值c时,最内层循环次数为:1+2+…+c。故$O(\sum_i^n i*(n-i-1))$但不会算,不过有个结论可以用:$\sum_{i=1}^n\sum_{j=1}^i\sum_{k=1}^j l=O(\frac{n^3}{6})=O(n^3)$
另外总结一些常用数学公式:
等差数列求和公式:$Sn = \frac{a_1+a_n}{2}n=a_1n+\frac{n(n-1)}{2}d$
等比数列求和公式:$Sn = a_1\frac{1-q^n}{1-q}$
和公式:$1+2+...+n=\frac{n(n+1)}{2}$
平方和公式:$1^2+2^2+...+n^2=\frac{n(n+1)(2n+1)}{6}$
立方和公式:$1^3+2^3+...+n^3=\frac{n^2(n+1)^2}{4}=(\frac{n(n+1)}{2})^2$
二、排序算法们:
2.1直接插入排序:
直接插入排序的思想是,在序列前一部分有序时,将后一部分最前面的元素插入到有序部分,使插入后的序列依然有序。(找位置,找位置过程中移动元素腾出位置,然后插入)具体的做法(文字描述就省略啦,自己看代码吧):
public static void directInsertSort(int[] a) {
int len = a.length;
int temp;
for(int i=1;i<len;i++) { //从第二个元素开始,因为一个元素时必然是有序的
if(a[i]<a[i-1]) {
temp = a[i];
int j=i-1;
for(;j>=0;j--) {
if(a[j]>temp) a[j+1]=a[j]; //不用>=是为了保证算法的稳定性,不然后出现的元素就会排在前出现的元素之前了,而且那样也会增加无谓的开销
}
a[j+1]=temp;
}
}
}
最差时间复杂度:应该出现在每次内循环中,都要找到最前面的位置插入,即,原数组是倒序的。这时运行频次t=0+1+2+...+(n-1)=n(n-1)/2。故O(n^2)。空间开销为常量们,O(1)。
2.2冒泡排序(基本交换排序):
冒泡排序的思想是,每次将最小的元素交换到队首。(对比左右元素,只要右元素小,就交换位置)。具体算法:
public static void bubbleSort(int[] a) {
int len = a.length;
int temp;
for(int i=0;i<len;i++) {
boolean flag=false; //表示本躺冒泡是否发生交换
for(int j=len-1;j>i;j--) {
if(a[j]<a[j-1]) { //稳定
temp=a[j];
a[j]=a[j-1];
a[j-1]=temp;
flag=true;
}
}
if(flag==false) return; //已经有序了,避免后续无用功
}
}
最差时间复杂度:倒序。同上O(n^2),O(1)。
2.3简单选择排序:
简单选择排序的思想是,每轮找到一个最小的数,和无序部分的第一个元素交换位置。(遍历后交换位置,不存在略过的元素的移动)。具体代码:
public static void selectionSort(int[] a) {
int len=a.length;
int temp;
for(int i=0;i<len;i++) {
int min = i;
for(int j=i+1;j<len;j++) {
if(a[j]<a[min]) min=j;
}
if(min!=i) {
temp=a[i];
a[i]=a[min];
a[min]=temp;
}
}
}
首先由于交换位置的原因,那么第一个最小元素(出现在靠后的位置)就会跑到前面,故不稳定。
时间复杂度方面相同,O(n^2),O(1)。
2.4希尔排序:
希尔排序是升级版的直接插入排序,它引入了步长的概念,先对等步长的每个子序列进行直接插入排序,然后缩短步长,最后步长=1,进行最后一次排序。
public static void shellSort(int[] a) {
int len = a.length;
int temp;
for(int dk=len/2;dk>=1;dk/=2) { //步长变化
for(int i=dk;i<=len;i++) {
if(a[i]<a[i-dk]) {//如果需要插入
temp=a[i];
int j=i-dk;
for(;j>=0;j-=dk) {
if(a[j]>temp) {a[j+dk]=a[j];}
}
a[j+dk]=temp;
}
}
}
}
希尔排序的时间复杂度不好计算,直接记结论,最坏O(n^2),平均O(n^1.3)。当相同关键字的元素被分到不同的子表时,顺序可能会发生变化,故不稳定。
2.5快速排序:
著名的快排,是交换排序的一种,思想是每轮把一个数放到它该在的位置(左边的元素都比它小,右边都比它大),即左右各一个指针,先从右边往左找,找到第一个比pivot(基准)小的值,停下,左指针往右找第一个比pivot大的元素,停下,交换两个元素,之后继续,直到两个指针相遇,右指针的位置的元素应小于pivot的值(右指针先走的原因),即为pivot应在的位置,右指针元素与pivot交换即可。还不明白可以参考http://wiki.jikexueyuan.com/project/easy-learn-algorithm/fast-sort.html的介绍。具体:
public static void quickSort(int[] a) {
forQuickSort(a,0,a.length-1);
}
public static void forQuickSort(int[] a,int l,int r) {
if(l==r) return;
int temp;
int pr=r,pl=l; //工作指针,先从右边开始
while(pr>pl) {
while(pr>pl&&a[pr]>=a[l]) pr--;
while(pr>pl&&a[pl]<=a[l]) pl++;
if(pr==pl) {temp=a[l];a[l]=a[pr];a[pr]=temp;}
else {temp=a[pr];a[pr]=a[pl];a[pl]=temp;}
}
forQuickSort(a,l,pr-1);
forQuickSort(a,pr+1,pr);
}
还有一种写法:
private int Partition(int [] input,int start,int end){
int p = input[start];
int left = start+1,right=end;
while(left<=right){
while(left<=right&&input[right]>=p) right--;
while(left<=right&&input[left]<=p) left++;
if(left<right){int temp=input[right];input[right]=input[left];input[left]=temp;}
else{input[start]=input[right];input[right]=p;}
}
return right;
}
当元素基本有序或者逆序时,快排会得到最坏时间复杂度,O(n^2)。如果比较顺利,快排两边比较均衡,则运行速度将大大提升,达到O(nlogn),而快排的平均时间复杂度和最佳表现接近,也是O(nlogn)。快排是内部排序算法中平均性能最优的排序算法。
对于空间复杂度,快排是递归的,故需要一个递归工作栈,最坏O(n),平均O(logn)。
因为存在左右指针相互交换的情况,所以快排是不稳定的。
关于对多个有序数组合并排序,看mergesort,时间复杂度O(m+n)
https://www.cnblogs.com/kkun/archive/2011/11/23/merge_sort.html
https://www.cnblogs.com/chengxiao/p/6194356.html
关于快速排序时间复杂度证明:
https://www.cnblogs.com/fengty90/p/3768827.html
常见排序算法总结:插入排序,希尔排序,冒泡排序,快速排序,简单选择排序以及java实现的更多相关文章
- 八大排序算法的python实现(八)简单选择排序
代码: #coding:utf-8 #author:徐卜灵 # L = [6, 3, 2, 32, 5, 4] def Select_sort(L): for i in range(0,len(L)) ...
- Python实现八大排序(基数排序、归并排序、堆排序、简单选择排序、直接插入排序、希尔排序、快速排序、冒泡排序)
目录 八大排序 基数排序 归并排序 堆排序 简单选择排序 直接插入排序 希尔排序 快速排序 冒泡排序 时间测试 八大排序 大概了解了一下八大排序,发现排序方法的难易程度相差很多,相应的,他们计算同一列 ...
- 冒泡排序与简单选择排序——Java实现
1.冒泡排序 1)原理说明:反复遍历要排序的数列,一次比較两个元素,假设他们的顺序错误就把他们交换过来.走訪数列的工作是反复地进行直到没有再须要交换,也就是说该数列已经排序完毕. 2)代码实现: pa ...
- Java学习笔记day07_琐碎知识_水仙花数_ASCII码_冒泡排序_简单选择排序_折半查找
琐碎知识: 水仙花数, ASCII码, 冒泡排序, 简单选择排序, 折半查找 1.水仙花数 每位数的平方的和等于本身. 如100到999之间的水仙花数满足: 个位的平方+十位的平方+百位的平方 = 本 ...
- 冒泡排序Vs直接选择排序
什么是排序?为什么要使用排序?事实上我们生活中处处都用到了排序.拿字典来说,如今,我们要在字典中查找某个字(已经知道这个字的读音),首先.我们须要依据这个字的读音,找到它所所在文件夹中的位置,然后依据 ...
- python实现排序算法 时间复杂度、稳定性分析 冒泡排序、选择排序、插入排序、希尔排序
说到排序算法,就不得不提时间复杂度和稳定性! 其实一直对稳定性不是很理解,今天研究python实现排序算法的时候突然有了新的体会,一定要记录下来 稳定性: 稳定性指的是 当排序碰到两个相等数的时候,他 ...
- C数据结构排序算法——直接插入排序法用法总结(转http://blog.csdn.net/lg1259156776/)
声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 排序相关的的基本概念 排序:将一组杂乱无章的数据按一定的规律顺次排列起来. 数据表( data list): ...
- java排序算法(四):冒泡排序
java排序算法(四):冒泡排序 冒泡排序是计算机的一种排序方法,它的时间复杂度是o(n^2),虽然不及堆排序.快速排序o(nlogn,底数为2).但是有两个优点 1.编程复杂度很低.很容易写出代码 ...
- 冒泡排序算法和简单选择排序算法的js实现
之前已经介绍过冒泡排序算法和简单选择排序算法和原理,现在有Js实现. 冒泡排序算法 let dat=[5, 8, 10, 3, 2, 18, 17, 9]; function bubbleSort(d ...
随机推荐
- centos通过yum安装mysql
前言 前天按照Oracle上的文档装了一遍mysql,选了最新8.0的版本,后来出现一些问题,网上搜答案,出来的基本还是5.x版本的解决方案,并不适用8.0版本.然后我就去看了一下公司的正式环境买的阿 ...
- [Vue] vue中setInterval的问题
vue中使用setInterval this.chatTimer = setInterval(() => { console.log(this.chatTimer); this.chatMsg( ...
- 【转载】 Sqlserver中通过Select Into语句快速单表备份
在Sqlserver数据库中,备份数据的方式有很多种,可以使用整个数据库备份,也可使用导出包含数据和架构的脚本文件的方式来进行单表或多表数据的备份,其实还有一种Select Into的方式可以快速备份 ...
- base64字符串转文件,以及ngImgCrop裁剪图片并上传保存到服务器示例
base64字符串是包含文件格式的文件字符串,例如:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAgAElE ...
- PHP接口APP接口
使用PHP来生成APP接口数据是非常简单的,如果你还不了解PHP没有关系,只需要看过PHP的基本语法,再看本示例就可以了. APP接口一般都是json格式(当然也有少数xml格式)遵循restful规 ...
- QT通过url下载图片到本地
/* strUrl:下载图片时需要的url strFilePath:下载图片的位置(/home/XXX/YYY.png) */ void ThorPromote::downloadFileFromUr ...
- Win10系统下装Ubuntu虚拟机的遇到的问题总结
环境和工具 win10操作系统 VMware Workstation 12 Ubuntu 14.0 64位 教程可参考:VMware Ubuntu安装详细过程(非常靠谱) [因为我的安装过程不是十分顺 ...
- dede二级导航
{dede:channelartlist} {dede:field name='typeurl'/}'——{dede:field name='typename'/} {dede:channel typ ...
- Spark RPC框架源码分析(二)RPC运行时序
前情提要: Spark RPC框架源码分析(一)简述 一. Spark RPC概述 上一篇我们已经说明了Spark RPC框架的一个简单例子,Spark RPC相关的两个编程模型,Actor模型和Re ...
- 重庆3Shape Dental System技术支持
Dental System 2014中的一些新的功能:为提高生产力增添了自动冠功能软件会自动根据位置设计冠的形状,以适应周围的牙齿和拮抗剂.新的强大的用户体验优化了工作流程和一个新的重新设计的用户界面 ...