归并排序算法

归并排序

      • 基本思想
      • 二路归并排序分治策略
        • 自底向上的二路归并算法
        • 自顶向下的二路归并算法
      • 归并排序应用

基本思想

首先将a[0…n-1]看成是n个长度为1的有序表,将相邻的k(k≥2)个有序子表成对归并,得到n/k个长度为k的有序子表,然后再将这些有序子表继续归并,得到n/k2个长度为k2的有序子表,如此反复进行,最后得到一个长度为n的有序表。
若k=2,即归并在相邻的两个有序子表中进行,称为二路归并排序,若k>2,即归并操作在相邻的多个有序子表中进行,称为多路归并排序。

  • 时间复杂度:T(n) = O(nlog2n)

二路归并排序分治策略

自底向上的二路归并算法

循环⌈log2n⌉次,length依次取1,2,4…每次执行以下步骤:

  1. 分解: 将原序列分解成length长度的若干子序列
  2. 合并: 将相邻的两个子序列调用Merge算法排序产生一个有序子序列
    归并排序算法_第1张图片
//相邻两个子表的合并
void Merge(int a[],int low,int mid,int high){
    //a[low..mid]+a[mid+1..high]=a[low..high]
    int* tmpa;
    int i = low;
    int j = mid+1;
    int k = 0;
    tmpa = (int*)malloc((high-low+1)*sizeof(int));
    while(i<=mid&&j<=high){
        if(a[i]<=a[j]){ //将第1子表中元素放入tmpa
            tmpa[k] = a[i];
            ++i;
            ++k;
        }
        else{   //将第2子表中元素放入tmpa
            tmpa[k] = a[j];
            ++j;
            ++k;
        }
    }
    while(i<=mid){
        tmpa[k] = a[i];
        ++i;
        ++k;
    }
    while(j<=high){
        tmpa[k] = a[j];
        ++j;
        ++k;
    }
    for(k = 0,i = low;i<=high;++i,++k)
        a[i] = tmpa[k];
    free(tmpa);
}

//一趟二路归并排序
void MergePass(int a[],int length,int n){
    int i;
    for(i = 0;i+2*length-1<n;i+=2*length)   //归并length长的两相邻子表
        Merge(a,i,i+length-1,i+2*length-1);
    if(i+length-1<n)    //余下两个子表,后者长度小于length
        Merge(a,i,i+length-1,n-1);  //归并这两个子表
}

//二路归并算法
void MergeSort(int a[],int n){
    int length;
    for(length = 1;length<n;length=2*length)
        MergePass(a,length,n);
}

示例结果
a[8]={1,3,9,5,6,4,7,2};

第1趟归并排序结果为:1  3  5  9  4  6  2  7
第2趟归并排序结果为:1  3  5  9  2  4  6  7
第3趟归并排序结果为:1  2  3  4  5  6  7  9

Process returned 0 (0x0)   execution time : 0.056 s
Press any key to continue.
自顶向下的二路归并算法

归并排序算法_第2张图片
设归并排序的当前区间是a[low…high],则递归归并的两个步骤如下:

  1. 分解: 将序列a[low…high]一分为二,即求mid=(low+high)/2,递归地对两个子序列a[low…mid]和a[mid+1…high]进行继续分解,最终条件是子序列长度为1
  2. 合并: 与分解过程相反,将已排序的两个子序列a[low…mid]和a[mid+1…high]归并为一个有序序列a[low…high]
//二路归并算法
void MergeSort(int a[],int low,int high){
    int mid;
    if(low<high){   //子序列有两个或两个以上元素
        mid = (low+high)/2; //取中间位置
        MergeSort(a,low,mid);   //对a[low..mid]子序列排序
        MergeSort(a,mid+1,high);    //对a[mid+1..high]子序列排序
        Merge(a,low,mid,high);  //将两子序列合并,见上面算法
    }
}

归并排序应用

问题描述:设a为一个含n个整数的序列,整数各不相同,如果存在i j 使得1≤ia[j],则称一个逆序对(a[i],a[j]),一个序列的逆序对的个数为逆序数。
3,1,4,5,2中有逆序对(3,1) (3,2) (4,2) (5,2)
设计算法求给定数组a中逆序对的个数

解决方法:采用二路归并(这里使用自顶向下)
在合并过程中,当a[i]≤a[j]时,不产生逆序对
当a[i]>a[j]时,在前半部分中比a[i]大的元素都比a[j]大,对应的逆序对数为mid-i+1,即逆序对为(a[i],a[j]),…,(a[mid],a[j])
修改Merge函数:
当a[i]>a[j]时,全局变量ans+=mid-i+1; (初始为0)

//相邻两个子表的合并
void Merge(int a[],int low,int mid,int high){
    //a[low..mid]+a[mid+1..high]=a[low..high]
    int* tmpa;
    int i = low;
    int j = mid+1;
    int k = 0;
    tmpa = (int*)malloc((high-low+1)*sizeof(int));
    while(i<=mid&&j<=high){
        if(a[i]<=a[j]){ //将第1子表中元素放入tmpa
            tmpa[k] = a[i];
            ++i;
            ++k;
        }
        else{   //将第2子表中元素放入tmpa
            tmpa[k] = a[j];
            ans+=mid-i+1;	
            ++j;
            ++k;
        }
    }
    while(i<=mid){
        tmpa[k] = a[i];
        ++i;
        ++k;
    }
    while(j<=high){
        tmpa[k] = a[i];
        ++i;
        ++k;
    }
    for(k = 0,i = low;i<=high;++i,++k)
        a[i] = tmpa[k];
    free(tmpa);
}
  • ——————END-2022-03-07——————
  • 个人学习笔记,如有纰漏,敬请指正。
  • 感谢您的阅读。

你可能感兴趣的:(算法,c++,容器,链表,算法,排序算法)