排序——归并排序

版权声明:莉莉莉 https://blog.csdn.net/qq_41700151/article/details/83276753

归并排序又叫合并排序,是一种利用分治法的一种稳定的排序
1.简介
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

2.描述
归并排序具体算法描述如下(递归版本):
1、Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。
2、Conquer: 对这两个子序列分别采用归并排序。
3、Combine: 将两个排序好的子序列合并成一个最终的排序序列。
归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(NlogN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(NlogN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)也是效率比较高的。
在这里插入图片描述在这里插入图片描述核心代码
例题:有一个序列,通过交换相邻数直到升序,求最小次数

#include<iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <cstdlib>
#include <algorithm>
#define ll long long
#define INF 0x3f3f3f
const int MAX=1e6;
using namespace std;
ll a[500000+10];
ll temp[500000+10];
ll sum;
void Merge(int l,int r,int m)
{
    int i=l;
    int j=m+1;
    int k=l;
    while(i<=m&&j<=r)
    {
        if(a[i]>a[j])
        {
            sum+=m-i+1;
            temp[k++]=a[j++];
        }
        else
            temp[k++]=a[i++];
    }
    while(i<=m)
        temp[k++]=a[i++];         //temp作排序后的数组
    while(j<=r)
        temp[k++]=a[j++];
    for(i=l;i<=r;i++)
        a[i]=temp[i];
}
void mergesort(int l,int r)
{
    if(l<r)
    {
        int mid=(l+r)/2;
        mergesort(l,mid);           //分成只有一个数的n个小区间
        mergesort(mid+1,r);
        Merge(l,r,mid);               //归并小区间
    }
}
int main()
{
    ll n;
    ios::sync_with_stdio(false);
    while(scanf("%lld",&n)!=EOF)
    {
        if(n==0)              
            break;
        for(ll i=1; i<=n; i++)
            scanf("%lld",&a[i]);              
        sum=0;
        mergesort(1,n);
        printf("%lld\n",sum);
    }
    return 0;
}

例题1、
题目输入一个数组,数组元素的大小在0->999.999.999的范围内,元素个数为0-500000范围。题目要求通过相邻的元素的交换,使得输入的数组变为有序,要求输出交换的次数

#include<iostream>
#include<stdlib.h>
using namespace std;
void printArray(int arry[],int len)
{
    for(int i=0; i<len; i++)
        cout<<arry[i]<<" ";
    cout<<endl;
}
int MergeArray(int arry[],int start,int mid,int end,int temp[])//数组的归并操作
{
    //int leftLen=mid-start+1;//arry[start...mid]左半段长度
    //int rightLlen=end-mid;//arry[mid+1...end]右半段长度
    int i=mid;
    int j=end;
    int k=0;//临时数组末尾坐标
    int count=0;
    //设定两个指针ij分别指向两段有序数组的头元素,将小的那一个放入到临时数组中去。
    while(i>=start&&j>mid)
    {
        if(arry[i]>arry[j])
        {
            temp[k++]=arry[i--];//从临时数组的最后一个位置开始排序
            count+=j-mid;//因为arry[mid+1...j...end]是有序的,如果arry[i]>arry[j],那么也大于arry[j]之前的元素,从a[mid+1...j]一共有j-(mid+1)+1=j-mid

        }
        else
        {
            temp[k++]=arry[j--];
        }
    }
    cout<<"调用MergeArray时的count:"<<count<<endl;
    while(i>=start)//表示前半段数组中还有元素未放入临时数组
    {
        temp[k++]=arry[i--];
    }
    while(j>mid)
    {
        temp[k++]=arry[j--];
    }
    //将临时数组中的元素写回到原数组当中去。
    for(i=0; i<k; i++)
        arry[end-i]=temp[i];
    printArray(arry,8);//输出进过一次归并以后的数组,用于理解整体过程
    return count;
}
int InversePairsCore(int arry[],int start,int end,int temp[])
{
    int inversions = 0;
    if(start<end)
    {
        int mid=(start+end)/2;
        inversions+=InversePairsCore(arry,start,mid,temp);//找左半段的逆序对数目
        inversions+=InversePairsCore(arry,mid+1,end,temp);//找右半段的逆序对数目
        inversions+=MergeArray(arry,start,mid,end,temp);//在找完左右半段逆序对以后两段数组有序,然后找两段之间的逆序对。最小的逆序段只有一个元素。
    }
    return inversions;
}
int InversePairs(int arry[],int len)
{
    int *temp=new int[len];
    int count=InversePairsCore(arry,0,len-1,temp);
    delete[] temp;
    return count;
}
void main()
{
    //int arry[]={7,5,6,4};
    int arry[]= {1,3,7,8,2,4,6,5};
    int len=sizeof(arry)/sizeof(int);
    //printArray(arry,len);
    int count=InversePairs(arry,len);
    //printArray(arry,len);
    //cout<<count<<endl;
    system("pause");
}

猜你喜欢

转载自blog.csdn.net/qq_41700151/article/details/83276753