5.归并排序

5.归并排序

5.1归并排序的思想和复杂度

归并排序思想

归并排序主要是分治法的思想,有自顶向下和自底向上的归并排序。
归并排序通过递归将一个数组一分为二,然后将两个数组分别进行排序,对两个数组又进行划分,划分成四个数组,依次类推,直到将整个数组分解完毕。每次将数组进行二分后,需要将两个排序完毕的数组进行归并。例如,将8个数组归并成4个数组,4个数组归并成2个数组,2个数组归并成1个数组。最后就完成了整个数组的排序.

时间和空间复杂度

时间复杂度如表所示

算法 平均情况 最好情况 最差情况
冒泡 O(NlogN) O(NlogN) O(NlogN)

空间复杂度:O(N)

5.2归并排序的操作

假设有这样一个数组vec:

下标 0 1 2 3 4 5 6 7 8 9
数组 9 8 7 6 5 4 3 2 1 0

归并方法

先上代码

    void merge(vector<int> &aux,vector<int> &vec,int lo,int mid,int hi)
    {
        int i=lo,j=mid+1;
        for(int k=lo;k<=hi;k++)
        {
            aux[k]=vec[k];
        }
        for(int k=lo;k<=hi;k++)
        {
            if(i>mid)               vec[k]=aux[j++];
            else if(j>hi)           vec[k]=aux[i++];
            else if(aux[i]>aux[j])  vec[k]=aux[j++];
            else                    vec[k]-aux[i++]; //if(aux[i]<=aux[j])
        }
        show(vec);
    }

归并排序中最关键的一步就是将两个子数组进行归并。上面的merge函数中,需要提供一个辅助数组aux,原数组vec,以及需要归并的索引lo,mid,hi。其中,lomid是前半部分子数组,mid+1hi是后半部分子数组。归并完成后就将原数组vec的vec[lo]~vec[hi]完成了排序。

自顶向下的归并排序

    void sort_ub(vector<int> &vec)
    {
        vector<int> aux(vec.size());
        sort_ub(aux,vec,0,vec.size()-1);
    }
    void sort_ub(vector<int> &aux,vector<int> &vec,int lo,int hi)
    {
        if(hi<=lo)return;
        int mid = lo+(hi-lo)/2;
        sort_ub(aux,vec,lo,mid);
        sort_ub(aux,vec,mid+1,hi);
        merge(aux,vec,lo,mid,hi);
    }

递归分治

  1. 每次sort_ub找到lo和hi中间的mid,
  2. 将前半部分vec[lo]~vec[mid]排序
  3. 将后半部分vec[mid]~vec[hi]排序
  4. 将vec[lo]~vec[hi]归并

自底向上的归并排序

    sort_bu(vector<int> &vec)
    {
        vector<int> aux(vec.size());
        sort_bu(aux,vec);

    }
    sort_bu(vector<int> &aux,vector<int> &vec)
    {
        int number=vec.size();
        for(int sz=1;sz<number;sz=sz+sz)//sz是子数组的大小
            for(int lo=0;lo<number-sz;lo=lo+sz+sz)
            //lo是子数组的最低位索引,首先需要确定lo
            //确定lo就可以根据lo和sz确定mid,确定mid就可以根据mid和sz确定hi,但边界条件还有个number-1,用来处理数组长度不为2次幂的情况
            {
                merge(aux,vec,lo,lo+(sz-1),lo+(sz-1)+sz<number-1?lo+(sz-1)+sz:number-1);
            }
    }

自底向上:

  1. 将整个数组进行两两划分,划分到子数组的长度为sz=1。

  2. 然后长度为sz的数组两两进行归并
    sz=1时,整个数组有N=vec.size()个子数组
    sz=2时,整个数组有N/2个子数组
    sz=4时,整个数组有N/4个子数组
    ..................
    sz=N时,整个数组有N/N=1个数组,这个时候就归并完毕了

  3. 边界条件:如果数组vec的长度不为2的幂,那么最后一次归并可能不是两两对称的,因此需要加一个边界条件lo+(sz-1)+sz<number-1?lo+(sz-1)+sz:number-1,目的就是为了将两个不对称的数组进行归并。

自底向上的归并过程中,前一次操作中已经完成排序,每一次的操作就是将分散的子数组合并成一个个更长的数组,最后完成整个数组的排序
OK, 可以上代码了。

5.3 C++代码

#include <iostream>
#include <vector>
using namespace std;

class Sort
{
public:
    void sort_ub(vector<int> &vec)
    {
        vector<int> aux(vec.size());
        sort_ub(aux,vec,0,vec.size()-1);
    }
    void sort_ub(vector<int> &aux,vector<int> &vec,int lo,int hi)
    {
        if(hi<=lo)return;
        int mid = lo+(hi-lo)/2;
        sort_ub(aux,vec,lo,mid);
        sort_ub(aux,vec,mid+1,hi);
        merge(aux,vec,lo,mid,hi);
    }
    sort_bu(vector<int> &vec)
    {
        vector<int> aux(vec.size());
        sort_bu(aux,vec);

    }
    sort_bu(vector<int> &aux,vector<int> &vec)
    {
        int number=vec.size();
        for(int sz=1;sz<number;sz=sz+sz)//sz是子数组的大小
            for(int lo=0;lo<number-sz;lo=lo+sz+sz)
            //lo是子数组的最低位索引,首先需要确定lo
            //确定lo就可以根据lo和sz确定mid,确定mid就可以根据mid和sz确定hi,但边界条件还有个number-1,用来处理数组长度不为2次幂的情况
            {
                merge(aux,vec,lo,lo+(sz-1),lo+(sz-1)+sz<number-1?lo+(sz-1)+sz:number-1);
            }
    }


    void merge(vector<int> &aux,vector<int> &vec,int lo,int mid,int hi)
    {
        int i=lo,j=mid+1;
        for(int k=lo;k<=hi;k++)
        {
            aux[k]=vec[k];
        }
        for(int k=lo;k<=hi;k++)
        {
            if(i>mid)               vec[k]=aux[j++];
            else if(j>hi)           vec[k]=aux[i++];
            else if(aux[i]>aux[j])  vec[k]=aux[j++];
            else                    vec[k]-aux[i++]; //if(aux[i]<=aux[j])
        }
        show(vec);
    }
    void show(vector<int> &vec)
    {
        for(int i=0;i<vec.size();i++)
        {
            cout << vec[i] << " ";
        }
        cout << endl;
    }
    vector<int> init()
    {
        vector<int> vec;
//        int arr[10]={4,3,5,2,1,9,8,6,0,7};
        int arr[10]={9,8,7,6,5,4,3,2,1,0};
        vec.insert(vec.begin(),arr,arr+10);
        cout <<"ori:"<<endl;
        show(vec);
        cout <<"-------------------"<<endl;
        return vec;
    }
    bool checkorder(vector<int> &vec)
    {
        for(int i=0;i<vec.size()-1;i++)
        {
            if(vec[i]>vec[i+1])return false;
        }
        return true;
    }
private:
    void exch(vector<int> &vec,int a,int b)
    {
        int tmp;
        tmp=vec[a];
        vec[a]=vec[b];
        vec[b]=tmp;
    }
};

int main()
{
    Sort sortob;
    vector<int> mvec=sortob.init();
//    sortob.sort_ub(mvec);
    sortob.sort_bu(mvec);
    cout <<"------result-------"<<endl;
    if(sortob.checkorder(mvec))
        sortob.show(mvec);
    else
    {   cout << sortob.checkorder(mvec);
    }
    return 0;
}

输出结果:

image.png

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

推荐阅读更多精彩内容

  • 归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非...
    爱情小傻蛋阅读 656评论 0 4
  • 背景 接上面四篇文章算法踩坑-快速排序 算法踩坑2-插入排序 算法踩坑3-堆排序 算法踩坑4-冒泡排序 来...
    aron1992阅读 353评论 0 0
  • 1 初级排序算法 排序算法关注的主要是重新排列数组元素,其中每个元素都有一个主键。排序算法是将所有元素主键按某种方...
    深度沉迷学习阅读 1,404评论 0 1
  • 最近在读< >时,了解到了很多常用的排序算法,故写一篇读书笔记记录下这些排序算法的思路和实现. 冒泡排序 冒泡排序...
    SylvanasSun阅读 686评论 0 0
  • 想要l过时间自由、有序高效的人生吗?预知详情,请扫码入群…… 最近被易效能®一位海归三胞胎妈妈刷了屏,2017年,...
    C3_EvaGao阅读 148评论 0 0