今天来总结一下常用的内部排序算法。内部排序算法们需要掌握的知识点大概有:算法的原理,算法的编码实现,算法的时空复杂度的计算和记忆,何时出现最差时间复杂度,以及是否稳定,何时不稳定。

首先来总结下常用内部排序算法:

类别 名称 时间复杂度(默认最坏情况) 空间复杂度 稳定性 备注
插入排序 直接插入排序  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实现的更多相关文章

  1. 八大排序算法的python实现(八)简单选择排序

    代码: #coding:utf-8 #author:徐卜灵 # L = [6, 3, 2, 32, 5, 4] def Select_sort(L): for i in range(0,len(L)) ...

  2. Python实现八大排序(基数排序、归并排序、堆排序、简单选择排序、直接插入排序、希尔排序、快速排序、冒泡排序)

    目录 八大排序 基数排序 归并排序 堆排序 简单选择排序 直接插入排序 希尔排序 快速排序 冒泡排序 时间测试 八大排序 大概了解了一下八大排序,发现排序方法的难易程度相差很多,相应的,他们计算同一列 ...

  3. 冒泡排序与简单选择排序——Java实现

    1.冒泡排序 1)原理说明:反复遍历要排序的数列,一次比較两个元素,假设他们的顺序错误就把他们交换过来.走訪数列的工作是反复地进行直到没有再须要交换,也就是说该数列已经排序完毕. 2)代码实现: pa ...

  4. Java学习笔记day07_琐碎知识_水仙花数_ASCII码_冒泡排序_简单选择排序_折半查找

    琐碎知识: 水仙花数, ASCII码, 冒泡排序, 简单选择排序, 折半查找 1.水仙花数 每位数的平方的和等于本身. 如100到999之间的水仙花数满足: 个位的平方+十位的平方+百位的平方 = 本 ...

  5. 冒泡排序Vs直接选择排序

    什么是排序?为什么要使用排序?事实上我们生活中处处都用到了排序.拿字典来说,如今,我们要在字典中查找某个字(已经知道这个字的读音),首先.我们须要依据这个字的读音,找到它所所在文件夹中的位置,然后依据 ...

  6. python实现排序算法 时间复杂度、稳定性分析 冒泡排序、选择排序、插入排序、希尔排序

    说到排序算法,就不得不提时间复杂度和稳定性! 其实一直对稳定性不是很理解,今天研究python实现排序算法的时候突然有了新的体会,一定要记录下来 稳定性: 稳定性指的是 当排序碰到两个相等数的时候,他 ...

  7. C数据结构排序算法——直接插入排序法用法总结(转http://blog.csdn.net/lg1259156776/)

    声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 排序相关的的基本概念 排序:将一组杂乱无章的数据按一定的规律顺次排列起来. 数据表( data list): ...

  8. java排序算法(四):冒泡排序

    java排序算法(四):冒泡排序 冒泡排序是计算机的一种排序方法,它的时间复杂度是o(n^2),虽然不及堆排序.快速排序o(nlogn,底数为2).但是有两个优点 1.编程复杂度很低.很容易写出代码 ...

  9. 冒泡排序算法和简单选择排序算法的js实现

    之前已经介绍过冒泡排序算法和简单选择排序算法和原理,现在有Js实现. 冒泡排序算法 let dat=[5, 8, 10, 3, 2, 18, 17, 9]; function bubbleSort(d ...

随机推荐

  1. 1.5准备CentOS和Nginx环境「深入浅出ASP.NET Core系列」

    准备CentOS 1.1虚拟机 CentOS的安装这里使用vmware workstation 14来做虚拟机,当然你也可以用Oracle的虚拟机或者不用虚拟机,使用云服务或者使用双系统,不是什么大问 ...

  2. 详解mybatis配置文件

    在前面两篇文章中,大致与大家分享了基于Java的ORM框架,Mybatis基本架构和Mybatis实现CRUD的基本原理与流程,在本篇文章中,继续与大家分享Mybatis配置文件. 通过研究Mybat ...

  3. Java设计模式系列-工厂方法模式

    原创文章,转载请标注出处:<Java设计模式系列-工厂方法模式> 一.概述 工厂,就是生产产品的地方. 在Java设计模式中使用工厂的概念,那就是生成对象的地方了. 本来直接就能创建的对象 ...

  4. Vcomputer简介

    1.Vcompter存储程序式计算机虚拟机软件简介   Vcompter存储程序式计算机虚拟机软件的文件名为comp_alpha(一般要先安装java运行环境,然后双击该软件即可运行),该软件是桂林电 ...

  5. idea git提交时候提示 --author 'java_suisui' is not 'Name ' and matches no existing author

    今天使用idea修改git项目的作者信息,提交时遇到错误: 0 files committed, 1 file failed to commit: test --author 'java_suisui ...

  6. nginx系列8:反向代理和负载均衡原理

    反向代理是nginx的一个非常重要的功能. 反向代理 nginx支持四层反向代理和七层反向代理,如下图. 负载均衡 负载均衡是实现服务高性能和高可用的重要手段,而nginx是实现负载均衡的重要工具.

  7. 前端入门20-JavaScript进阶之异步回调的执行时机

    声明 本系列文章内容全部梳理自以下几个来源: <JavaScript权威指南> MDN web docs Github:smyhvae/web Github:goddyZhao/Trans ...

  8. vue 项目中引用百度地图

    新建map.js export const BaiduMap = { init: function() { const BMapURL = 'https://api.map.baidu.com/api ...

  9. 44.Odoo产品分析 (五) – 定制板块(1) – 管理odoo安装(1)

    查看Odoo产品分析系列--目录 1 管理员的注意事项 在记录重要的配置细节时必须要小心,而且必须要有一个连续性的合适的.让系统能够安装备份并运行在一个可接受的时间内的计划. 1.1 制定实施策略 如 ...

  10. Android为TV端助力:UDP协议(接收组播和单播)

    private static String MulticastHost="224.9.9.98";private static int POST=19999;private sta ...