常见排序算法

基本概念

内部和外部排序

内部排序在这里指的是只用到了电脑内存而不使用外存的排序方式。相对的,外部排序就是同时动用了电脑内存和外存的排序方式。本文在这里只讨论内部排序。

分类

比较和非比较排序

比较在这里指的是需要比较两个元素的大小(前后)才能进行的排序。难道有排序算法不需要比较吗?的确有,但是不多。常见的有三种:计数排序,桶排序,基数排序。它们用统计的方法规避了比较,详细的可查看之后讲到的这些算法。

转换

每次只调换两个元素之间的位置。

插入

遍历到的元素放入之前维护的已完成排序的序列中。

选择

选择剩余元素中最大或最小的元素。

知道了以上概念后,就能更好的看懂分类了(建议先略看,看完所有排序算法后再回看)


稳定度 (Stability)

定义:如果排序算法并不改变两个相同值的元素的相对位置,则此算法稳定度高。

这张图十分形象地解释了以上定义:


冒泡排序 (Bubble Sort) 


1 算法

从第一个元素开始遍历,比较当前元素跟下一个元素的大小,如果不符合排序,交换位置。结束最后一个元素后,再从头开始不断遍历,直到完成排序。

剖析:每遍历完一次,最小数前进一位,但是最大数到达最终位;末尾已经是最终排序。

2 代码

基本

void bubble(vector<int>& arr){

    for(int i=0;i<arr.size()-1;i++){ //only need n-1 swaps to move the smallest to the front

        for(int j=0;j<arr.size()-1;j++){

            if(arr[j]>arr[j+1]) swap(arr[j],arr[j+1]);

            //arr[j]>arr[j+1] stable

            //arr[j]>=arr[j+1] unstable

            }

    }

}

优化1: 每遍历完一遍,看是否已经提前完成排序(用 hasSorted)。如是,提早结束。

void bubble1(vector<int>& arr){

    bool hasSorted = false;

    for(int i=0;i<arr.size()-1&&!hasSorted;i++){

        hasSorted=true;

        for(int j=0;j<arr.size()-1;j++){

            if(arr[j]>arr[j+1]){

                hasSorted = false;

                swap(arr[j],arr[j+1]);

            }

        }

    }

}

优化2:  根据“剖析:每遍历完一次,最小数前进一位,但是最大数到达最终位;末尾已经是最终排序。”,每遍历完一次,里面的loop就可以少遍历一个元素。但其实由此我们可以推论,最后一个swap的j和j+1, j之后的元素(不包括j)都已经完成排序了。

void bubble2(vector<int>& arr){

    int n = arr.size()-1;

    for(int i=0;i<arr.size()-1;i++){

        int upto = 0;

        for(int j=0;j<n;j++){ //j小于不定排序的最后一位

            if(arr[j]>arr[j+1]){

                upto = j; //upto=j不定大小的最后一位, j+1 已经完成排序(最后一个if)

                swap(arr[j],arr[j+1]);

            }

        }

        n=upto;

        if(n==0) break;

    }

}

优化3: 可以进行双向的循环,正向循环把最大元素移动到末尾,逆向循环把最小元素移动到最前,这种优化过的冒泡排序,被称为鸡尾酒排序(Cocktail Sort)

void bubble4(vector<int>& arr){

    int beg = 0;

    int end = arr.size()-1;

    while(beg<end){

        int nbeg = beg, nend = end;

        //正向循环

        for(int i=beg;i<end;i++){

            if(arr[i]>arr[i+1]){

                nend=i;

                swap(arr[i],arr[i+1]);

            }

        }

        if(nend==end) break;

        end = nend;

        //逆向循环

        for(int i=end; i>beg;i--){

            if(arr[i]<arr[i-1]){

                nbeg=i;

                swap(arr[i], arr[i-1]);

            }

        }

        if(nbeg==beg) break;

        beg = nbeg;

    }

}

3 分析

3.1 稳定度

决定于比较的时候用的是大于等于(小于等于)还是大于(小于)

arr[i]>arr[i+1] --> 稳定

arr[i]>=arr[i+1] --> 不稳定

3.2 时间

逆向排序时是最差的情况,O(n^2)

3.3 空间

需要O(1)来完成swap等

快速排序 (Quicksort)


1 算法

利用了 divide & conquer 的思想。

在序列中任意选择一个数,然后把序列分成比这个数大的和比这个数小的两个子序列。不断重复以上步骤完成排序。

2 代码

int partition1(vector<int>& arr, int beg, int end){

    int pivot = arr[beg];

    int l=beg+1, r=end;

    while(l<=r){

        if(arr[l]<pivot) l++;

        else if(arr[r]>pivot) r--;

        else if(arr[l]>=pivot && arr[r]<=pivot){

            swap(arr[l++], arr[r--]);

        }

    }

    swap(arr[r], arr[beg]);

    return r;

}

int partition2(vector<int>& arr, int beg, int end){

    int pivot = arr[beg];

    int index = beg+1;

    for(int i=index;i<=end;i++){

        if(arr[i]<pivot){

            swap(arr[i], arr[index++]);

        }

    }

    swap(arr[beg],arr[index-1]);

    return index-1;

}

void quick(vector<int>& arr, int beg, int end){

    if(beg<end){

        int pivot = partition1(arr,beg,end);

        // int pivot = partition2(arr,beg,end);

        quick(arr, beg, pivot-1);

        quick(arr, pivot+1, end);

    }

}

优化1 切换到插入排序

因为快速排序在小数组中也会递归调用自己,对于小数组,插入排序比快速排序的性能更好,因此在小数组中可以切换到插入排序。

优化2 三数取中

最好的情况下是每次都能取数组的中位数作为切分元素,但是计算中位数的代价很高。人们发现取 3 个元素并将大小居中的元素作为切分元素的效果最好。

优化3 三向切分

对于有大量重复元素的数组,可以将数组切分为三部分,分别对应小于、等于和大于切分元素。

等之后review的时候再贴code吧~ 写这个太烧脑了~

3 分析

3.1 稳定度

3.2 时间

最好的情况是pivot都是中点 -- O(n log n) (平均情况也是如此,所以有些快排算法会在一开始随意打乱数列)

最差的情况是本来就是有序数列 -- O(n^2)

3.3 空间

尽管没有用另外的数据结构,但是不是O(1)哦~ 因为recursion需要在stack frames里面重新建array。我上面的code需要O(n) extra space, 最优的话,可以达到O(log n)

4 变形

LeetCode - Top K Frequent Elements

插入排序 (Insertion Sort)


1 算法

维护一段有序数列,同时遍历待排序的数列,在有序数列里找到合适的位置,插入元素。

2 代码

void insertion(vector<int>& arr){

    for(int i=1;i<arr.size();i++){

        int temp=i;

        for(int j=i-1;j>=0;j--){

            if(arr[temp]<arr[j]) swap(arr[temp--],arr[j]);

        }

    }

}

优化1 找到当前元素该插入的位置后,再插入

void insertion1(vector<int>& arr){

    for(int i=1;i<arr.size();i++){

        int temp=arr[i];

        int j=i-1;

        for(;j>=0 && temp<arr[j];j--){

            arr[j+1] = arr[j];

        }

        arr[j+1] = temp;

    }

}

3 分析

3.1 稳定度

arr[temp]<arr[j], 只有小于的时候swap,等于的时候保持先前的相对位置,所以是稳定的。

3.2 时间

最优情况是正向排序 -- O(n)

最差是逆向排序,每次插入都需要比较已完成数列元素的个数 -- O(n^2)

3.3 空间

O(1) - 如上code -> temp


原文链接:https://blog.csdn.net/real_lisa/article/details/82685407

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354