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

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

类别 名称 时间复杂度(默认最坏情况) 空间复杂度 稳定性 备注
插入排序 直接插入排序  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. 补习系列(13)-springboot redis 与发布订阅

    目录 一.订阅发布 常见应用 二.Redis 与订阅发布 三.SpringBoot 与订阅发布 A. 消息模型 B. 序列化 C. 发布消息 D. 接收消息 小结 一.订阅发布 订阅发布是一种常见的设 ...

  2. 并发系列(1)之 Thread 详解

    本文主要结合 java.lang.Thread 源码,梳理 Java 线程的整体脉络: 一.线程概述 对于 Java 中的线程主要是依赖于系统的 API 实现的,这一点可以从 java.lang.Th ...

  3. 页面优化,谈谈重绘(repaint)和回流(reflow)

    一.前言 偶尔在面试过程中遇到过重汇与回流reflow的问题,毕竟页面优化也是考核一个开发者能力的关键之一,上篇文章聊了下documentfragment也是为了减轻回流问题,那么本篇文章好好介绍下重 ...

  4. 配置多版本jdk

    配置办法https://blog.csdn.net/qq342643414/article/details/78364601 可能会遇到的问题https://www.cnblogs.com/chuij ...

  5. 给dao层注入jdbcTemplate时的一个强行bug(jdbcDaoSupport不要随便用!用了要记得!)

    记录Dao层一个鱼唇至极的错误 这一天我在使用Spring的进行注解配置项目时, 我的Idea给我抛了一个如下的错误: Exception in thread "main" org ...

  6. HTML文档命名规则

    HTML文档是展示Web前段开发工程师成果的最好表示方式,为了便于文档规范化管理,在编写HTML文档时,必须遵循HTML文件命名规则. HTML文档命名规则如下: (1)文档的扩展名为htm或者htm ...

  7. Testlink插件工具

    目的: 使用Testlink时间长了,会发现有些功能体验不是很好,比如用例编写就无法快速复制,且展示能力很弱 使用对象: 测试人员.测试leader,技术经理 xmind2testlink:xmind ...

  8. java:编程比赛中有用的方法整理(一)数组

    我曾经参加过几次编程比赛,但是当时用的是c语言,现在学习了java,打算专攻java组,故以此整理. 数组无论在哪里都必不可少. 一.数组的拷贝: 使用Arrays类的copyOf方法: 1.将一个数 ...

  9. 如何正确的升级node版本【已解决】

    博主亲身经历可以用以下几步解决node版本问题噢,解决了记得留个赞哈! 1 . 首先要安装n这个版本控制器噢 npm i -g n 如果这一步成功了就接着走哈,但是博主试了,大多数是安装不成功的,可以 ...

  10. WPF软件开发系统之六——药品管理查询系统

    本系统使用.Net WPF开发,运行于Windows操作系统,电脑或者触摸屏设备. 功能主要是药品按照各类条件检索及展示. 开发环境及工具: 首页: 按关键字查询: 按功效查询: 还有其它按拼音.按笔 ...