常见排序算法总结

选择排序

优点:容易实现,原地排序不需要额外的存储空间

缺点:扩展性差

void SelectSort(){
    int [] array={1,5,3,2,6,7,9,13,54,20};
    int min=0;//保存最元素值的下标
    for(int i=0;i<array.length-1;i++){
        min=i;
        //查找最小数在数组中的下标
        for(int j=i+1;j<array.length;j++){
            if(array[min]>array[j]){
                min=j;//保存最小数的下标
            }
        }
        //如果第i个最小的数位置不在i上,则进行交换
        if(i!=min){
            int temp=array[i];
            array[i]=array[min];
            array[min]=temp;
        }
    }
}

冒泡排序

按照改进的算法,对于一个已经有序的数组,算法完成第一次外层循环后就会返回。
实际上只发生了 N - 1次比较,所以最好的情况下,该算法复杂度是O(n)。

void BubbleSort(){
    int [] array={1,5,3,2,6,7,9,13,54,20};
    for(int i=0;i<array.length-1;i++){
        //每一轮比较的次数为N-1-i;
        for(int j=0;j<array.length-1-i;j++){
            //比较相邻的两个数,小靠前
            if(array[j]>array[j+1]){
                //两个数做交换.通过设置临时变量
                int temp=array[j];
                array[j]=array[j+1];
                array[j+1]=temp;
            }
        }
    }
}
void BubbleSortImproved(){
    int [] array={1,5,3,2,6,7,9,13,54,20};
    boolean swapped=true;
    for(int i=0;i<array.length-1&&swapped;i++){
        swapped=false;
        //每一轮比较的次数为N-1-i;
        for(int j=0;j<array.length-1-i;j++){
            //比较相邻的两个数,小靠前
            if(array[j]>array[j+1]){
                //两个数做交换.通过设置临时变量
                int temp=array[j];
                array[j]=array[j+1];
                array[j+1]=temp;
                swapped=true;
            }
        }
    }
}

插入排序

优点:实现简单,数据量少时效率高

​ 如果输入序列已经预排序,时间复杂度为O(n+d),d是反转的次数。

​ 算法运行效率优于选择排序冒泡排序即使是最坏的情况三个算法时间复杂度均为O($n^2$)

​ 能在接收序列的同时进行排序

void InsertSort(){
    int [] array={20,25,15,42,36,16,12};
    for(int i=1;i<array.length;i++){
        int temp=array[i];
        //把下标保存起来
        int j=i;
        while(j>0&&temp<array[j-1]){
            //上面的数覆盖其下面的数
            array[j]=array[j-1];
            j--;
        }
        array[j]=temp;//插入数据
    }
}

希尔排序

希尔排序是基于直接插入排序的,直接插入排序在元素较少和元素基本有序时效率是不错的,但随着元素增多和有序性破坏,效率会下降的很明显。希尔排序通过分组实现跳跃式移动,保证待排序序列基本有序后再排序,就能提高效率。

void ShellSort(){
    int[]a={49,38,65,97,76,13,27,49,78,34,12,64,1};
    int d=a.length;
    while(true){
        d=d/2;
        for(int x=0;x<d;x++) {
            for(int i=x+d;i<a.length;i=i+d) {
                int temp=a[i];
                int j;
                for(j=i-d;j>=0&&a[j]>temp;j=j-d) {
                    a[j+d]=a[j];
                }
                a[j+d]=temp;
            }
        }
        if(d==1) {
            break;
        }
    }
}

归并排序

归并排序的主要操作是归并,其主要思想是:将若干有序序列逐步归并,最终得到一个有序序列。

int[] sort(int[] o,int m,int n){
    int mid;
    int[] result = new int[o.length];
    if(o.length == 1|| m==n){
        result[0] = o[0];
    }else{
        mid = (m+n)/2;
        int[] temp1 = new int[mid-m+1];
        int[] temp2 = new int[o.length-mid+m-1];
        System.arraycopy(o,0,temp1,0,mid-m+1);
        System.arraycopy(o,mid-m+1,temp2,0,o.length-mid+m-1);
        int[] result1 = sort(temp1,m,mid);
        int[] result2 = sort(temp2,mid+1,n);
        result = Merge(result1,result2);
    }
    return result;
}
int[] Merge(int[] i,int[] j){
    int m=0,n=0,k=0;
    int[] result = new int[i.length+j.length];
    for(; m<i.length&&n<j.length; k++){
        if(i[m]<j[n]){
            result[k] = i[m++];
        }else{
            result[k] = j[n++];
        }
    }
    if(m<i.length){
        while(m<i.length){
            result[k++] = i[m++];
        }
    }
    if(n<j.length){
        while(n<j.length){
            result[k++] = j[n++];
        }
    }
    return result;
}

快速排序

快速排序的思想是分割,是分治算法技术的一个实例。确保一个元素左边的元素都小于这个元素,右边的元素都大于这个元素,然后对这两部分分别继续进行分割,从而达到排序的效果。

void Quicksort(int[] a,int low,int high){
    int temp;
    if(low<high){
        temp = partition(a,low,high);
        Quicksort(a,low,temp-1);
        Quicksort(a,temp+1,high);
    }
}

int partition(int[] a,int low,int high){
    int i=low;
    int j=high;
    while(i<j){
        while(i<j&&a[i]<=r[j]) j--;//右侧扫描
        if(i<j){swap(a,i,j);i++;}//小记录置前
        while(i<j&&a[i]<=r[j]) i++;//左侧扫描
        if(i<j){swap(a,i,j);j--}//大记录置后
    }
    return low;
}
void swap(int[] a,int low,int high){
    if(low<high){
        int temp=a[low];
        a[low]=a[high];
        a[high]=a[low];
    }
}

堆排序

基于比较的排序,属于选择排序,优点是最坏的情况下O($n\log n$)

基本思想:首先将待排序的记录序列构造成一个堆,此时,选出了堆中所有记录的最大者,然后将它从堆中移走,并将剩余的记录再调整成堆,这样又找出了次小的记录,以此类推,直到堆中只有一个记录。

 void HeapSort(int[] a,int n){
        for(int i=n/2; i>=1; i--){
            heapAdjust(a,i,n);//从最后一个有子节点的节点开始依次往前调整对应节点来生成大顶堆
        }
        for(int i=1; i<n; i++){
            swap(a,1,n-i-1);//交换堆顶元素与未排序堆最后一个元素
            heapAdjust(a,1,n-i);//根据调整节点重新生成大顶堆
        }
}
    void heapAdjust(int r[], int k, int m ){
        //要筛选结点的编号为k,堆中最后一个结点的编号为m
        int i=k;
        int  j=2*i;//到达下一层的左孩子
        while (j<=m ){           //筛选还没有进行到叶子
            if (j<m && r[j]<r[j+1]) j++;  //左右孩子中取较大者
            if (r[i]>r[j]) break;
            else {
                swap(r,i,j);
                i=j;
                j=2*i;
            }
        }

    }

线性排序-计数排序

计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。

牺牲空间换取时间,当K=O(n)时计数排序速度快,否则复杂度将会更高

int[] CountSort(int[]a){
        int b[] = new int[a.length];
        int max = a[0],min = a[0];
        for(int i:a){
            if(i>max){
                max=i;
            }
            if(i<min){
                min=i;
            }
        }
        int k=max-min+1;//这里k的大小是要排序的数组中,元素大小的极值差+1
        int c[]=new int[k];
        for(int i=0;i<a.length;++i){//O(n)
            c[a[i]-min]+=1;//优化过的地方,减小了数组c的大小
        }
        for(int i=1;i<c.length;++i){//O(k)
            c[i]=c[i]+c[i-1];
        }
        for(int i=a.length-1;i>=0;--i){//O(n)
            b[--c[a[i]-min]]=a[i];//按存取的方式取出c的元素
        }
        return b;
    }

线性排序-桶排序

与计数排序类似,桶排序也对输入加以限制来提高算法性能。换言之。如果输入的序列来自固定集合,则桶排序的效率较高。例如假设所有输入元素是在【0,k-1】上的整数集合,这就表示k是输入序列中最远距离元素的数目。桶排序采用K个计数器,第i个计数器记录第i个的元素出现次数。

static int BUCKET=10;
void BucketSort(int A[],int array_size){
    int[] bucket=new int[BUCKET];
    for(int i=0;i<BUCKET;i++)bucket[i]=0;
    for(int i=0;i<array_size;i++)++bucket[A[i]];
    for(int i=0,j=0;j<BUCKET;j++){
        for(int k=bucket[j];k>0;--k){
            A[i++]=j;
        }
    }
}

线性排序-基数排序

1)取每个元素最低有效位

2)基于最低有效位对序列中的元素进行排序,并保持具有相同位的元素的原有次序(稳定排序)

3)对下一个最低有效位重复该过程

基数排序速度取决于内部基本操作。如果输入值具有的位数长度不等。还需要对附加位进行排序,这是基数排序最慢的部分之一,也是最难进行效率优化的部分之一。

算法灵活性不如其他排序算法,对于每一种不同类型数据,基数排序都需要重写,难以编写一个处理所有数据的通用基数排序算法。

void RadixSort(int[] number, int d){ //d表示最大的数有多少位
    int k = 0;
    int n = 1;
    int m = 1; //控制键值排序依据在哪一位
    int[][]temp = new int[10][number.length]; //数组的第一维表示可能的余数0-9
    int[]order = new int[10]; //数组orderp[i]用来表示该位是i的数的个数
    while(m <= d) {
        for(int i = 0; i < number.length; i++) {
            int lsd = ((number[i] / n) % 10);
            temp[lsd][order[lsd]] = number[i];
            order[lsd]++;
        }
        for(int i = 0; i < 10; i++) {
            if(order[i] != 0)
                for(int j = 0; j < order[i]; j++) {
                    number[k] = temp[i][j];
                    k++;
                }
            order[i] = 0;
        }
        n *= 10;
        k = 0;
        m++;
    }
}

性能比较

排序算法 平均时间复杂度 最好情况 最坏情况 空间复杂度 稳定性
选择排序 O(n^2) O(n^2) O(n^2) O(1) 不稳定
冒泡排序 O(n^2) O(n) O(n^2) O(1) 稳定
插入排序 O(n^2) O(n) O(n^2) O(1) 稳定
希尔排序 O(n*log n)~O(n^2) O(n^{1.3}) O(n^2) O(1) 不稳定
归并排序 O(n*log n) O(n*log n) O(n*log n) O(n) 稳定
快速排序 O(n*log n) O(n*log n) O(n^2) O(log n)~O(n) 不稳定
堆排序 O(n*log n) O(n*log n) O(n*log n) O(1) 不稳定
线性排序-计数排序 O(n+k) O(n+k) O(n+k) O(1) 稳定
线性排序-桶排序 O(n+k) O(n+k) O(n^2) O(n+k) 稳定
线性排序-基数排序 O(n*k) O(n*k) O(n*k) O(n+k) 稳定
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • 概述:排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    每天刷两次牙阅读 3,704评论 0 15
  • 你是否有这样的感觉:每天一睁开眼,就开始关注财经资讯,理财群里聊天记录,达人的理财文章,专家的最新评论,总害怕错失...
    如是小课堂阅读 199评论 0 0
  • 今年因二宝太小,回农村老家过年太冷不方便,所以我和二宝就没有回老家过年。腊月二十九,爱人和儿子就开车回农村老家去了...
    Donny悠悠阅读 249评论 2 4
  • 我是谁?这是一个充满思辨的哲学问题,我们应该认真思考。 1 一位...
    漫步者说事阅读 1,214评论 0 3