【数据结构】排序之归并排序

4 篇文章 0 订阅
3 篇文章 0 订阅

归并排序

归并,即“递归合并”。

归并排序的主要思路

Q:我们排序的目的是什么?
A:让无序的序列变得有序。

比如说,下面有一个序列:

排序前排序后
76543211234567

如果我们将这个序列从中间附近分开,分成两个子序列,想办法让这两个子序列变得有序。然后再把两个有序的子序列合并成一个有序的子序列,那么我们的排序不就完成了吗?

如下图:
在这里插入图片描述
这样一个大的任务被分成了两个子任务:对两个子序列进行排序

排序前排序后
76544567
321123

递归

很容易发现,这两个子任务和原来的任务没有本质区别,只是规模变得更小了。可以对这两个子任务再次进行拆分,在代码实现上,这就是递归的操作。

一个基本事实:当序列中只有一个元素时,它是一个有序的序列

有了这个基本事实,不难想出:只要经过足够多次数的分割,一定会将序列分成只有一个元素的序列,这就是递归的出口。

如此,可以给出归并排序主体思想的代码:
这个代码十分简洁:

void MergeSort(int arr[],int start,int end,int temp[])//传入的参数:待排序数组,起始下标,终止下标,辅助数组temp
{
    if(start>=end)return;//递归出口
    int mid=start+(end-start)/2;//定位到中间附近的下标
    MergeSort(arr, start, mid, temp);//对中点及中点以前的元素进行递归
    MergeSort(arr, mid+1, end, temp);//对中点以后的元素进行递归
    Merge(arr,start,mid,end,temp);//对两个有序的序列进行合并
}

合并

那么,如何进行合并的操作呢?

用两个下标(i,j),分别指向两个有序序列的起始点,再用一个下标(k),指向辅助数组temp的相应位置。
比较i和j对应元素的大小,因为是要排成一个递增的序列,选取较小的赋值给temp[k],再将这个下标和k都加1。
当两个序列都走完后,将temp中的元素复制回待排序的数组。

两个序列中有相等关键词怎么办?

显然,i是严格小于j的,所以i对应的元素在序列中的相对位置是要比j对应的元素靠前的。
k是从小往大走,为了保证排序的稳定性,当关键词相等时,应该选取i对应的元素,再将i加1。

排序的稳定性: 如果关键词相等的两个元素在排序前后相对位置不变,那么称这个排序算法是稳定的。

归并排序是个稳定的排序算法,稳定性由上述操作保证。

下面给出合并操作的代码实现:

void Merge(int arr[],int start ,int mid,int end,int temp[])
{
    int i=start;//定位左面的序列
    int j=mid+1;//定位右面的序列
    int k=start;//定位temp的位置
    while(i<=mid||j<=end)//当两个序列中有没走完的序列时,进入循环
        if(i<=mid&&j<=end)//如果两个序列都没走完
            temp[k++]=arr[i]<=arr[j]?arr[i++]:arr[j++];//选取小的元素赋值,并将下标++
        else
            temp[k++]=i<=mid?arr[i++]:arr[j++];//只有一个序列没走完,就哪个没走完要哪个
    for(int a=start;a<=end;a++)//将元素复制回待排序数组
        arr[a]=temp[a];
}

将合并的代码放在排序代码之前,就能执行了。

归并排序是一个时间复杂度为线性对数阶的排序算法,时间复杂度为:O(n*log2n)(以2为底n的对数),要占用至少n个元素的辅助空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值