第一章 基础算法 归并排序

1、算法思路

归并排序是一种基于分治思想的一种排序方法

(下面的讨论均是基于将数组从小到大排序)

具体步骤

  1. 直接从中间位置划分区间成左右两部分 (mid = (l + r) / 2)

  2. 分别递归两部分区间

  3. 归并处理左右两部分子区间(使用双指针)

2、模板代码

以下代码模板参考Acwing

//归并所用临时数组
int tmp[N];
void merge_sort(int q[], int l, int r)
{
  //递归的边界
  if(l >= r)return;
  //1、选择中间点为分界点,
  int mid = (l + r) >> 1;
  //2、直接递归处理
  merge_sort(q,l,mid);
  merge_sort(q,mid + 1,r);
  //3、利用双指针,归并处理左右区间
  //k是临时数组下标,i指向左边子区间起点,j指向右边子区间的起点
  int k = 0, i = l, j = mid + 1;
  while(i <= mid && j <= r)
  {
    if(q[i] <= q[j])
    {
      tmp[k ++] = q[i ++];
    }else{
      tmp[k ++] = q[j ++];
    }
  }
  //剩余的还未排序的部分
  while(i <= mid)tmp[k ++] = q[i ++];
  while(j <= r)tmp[k ++] = q[j ++];
  
  for(int i = 0; i < k; i ++)
  {
    q[l + i] = tmp[i];
  }
}

1、代码解释

上面的模板中,归并处理左右子区间是使用双指针进行的。

首先将指针i,j分别指向左右子区间的起点。此时左右区间的元素已经都是有序的了。当指针都在各自区间范围内移动时比较二者大小,小的一个暂存于临时数组tmp。当其中一个指针移出了区间范围时,另一个区间剩下的数字一定是全部大于他tmp里的数字的,可以直接加在tmp后面。

为什么左右区间已经是有序的呢?原因就是我们已经对左右区间进行递归了。

例如对于 2 1这个数组划分左右区间,mid = (0 + 1)/ 2 = 0。左区间元素 2,右区间元素 1。

一个区间只有一个元素的时候,一定是有序的,直接定为递归边界返回来。

此时i指向2,j指向1,二者进行归并之后的顺序就是1,2了。

2、具体示例

对于一个数组

6 9 3 5 2 1

首先,我们选择分界点,选择数组中间的位置为分界点,就是3,左区间元素(643)右区间元素(521)

6 9 `3` 5 2 1

然后分别递归,递归完之后,左右区间已经变得有序

然后做归并处理

  1. 归并初始状态
左区间369
指针位置i
右区间125
指针位置j
临时区间
指针位置k
  1. i、j都在各自的区间范围内活动
  • 上表中q[j] < q[i],各区间变为下表
左区间369
指针位置i
右区间125
指针位置j
临时区间1
指针位置k
  • 上表中q[j] < q[i],各区间变为下表
左区间369
指针位置i
右区间125
指针位置j
临时区间12
指针位置k
  • 上表中q[j] > q[i],各区间变为下表
左区间369
指针位置i
右区间125
指针位置j
临时区间123
指针位置k
  • 上表中q[j] < q[i],各区间变为下表
左区间369
指针位置i
右区间125
指针位置j (超出范围)
临时区间1235
指针位置k
  1. 未超出范围的区间元素直接补充到临时区间末尾,这里就是6、9
临时区间123569
指针位置k
  1. 将临时区间的值覆盖到原区间,则原区间也有序了

3、边界问题分析

1. 避免递归时区间无限划分问题

mid = (l + r) / 2,是向下取整,因为l != r,所以mid最大值也不能取到r,所以(l,mid)不会与(l,r)重复,导致无限循环。而mid的最小值是l,对于(mid + 1,r)也不会跟(l,r)相同,导致无限循环。

3、 算法复杂度分析

平均时间复杂度最坏时间复杂度最优时间复杂度平均空间复杂度稳定性
O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) O ( n ) O(n) O(n)​​稳定

1.时间复杂度分析

归并排序也是一种递归的算法,所以必须写出一个关于时间的递归关系式

假设N是需要排序的元素的数量,为了方便分析,我们假设N是2的幂。假设T(N)是N个元素使用归并排序所用的时间,则对于N个数的归并排序的用时等于完成两个大小为N/2的递归排序所用的时间加上合并的时间,是线性的,则有

T ( 1 ) = 1 T(1)=1 T(1)=1

T ( N ) = 2 T ( N / 2 ) + N T(N)=2T(N/2) +N T(N)=2T(N/2)+N

两边同除以N得到

T ( N ) N = T ( N / 2 ) N / 2 + 1 \frac{T(N)}{N}=\frac{T(N/2)}{N/2} + 1 NT(N)=N/2T(N/2)+1

该方程对于任意的N都是成立的,则有

T ( N / 2 ) N / 2 = T ( N / 4 ) N / 4 + 1 \frac{T(N/2)}{N/2}=\frac{T(N/4)}{N/4} + 1 N/2T(N/2)=N/4T(N/4)+1

T ( N / 4 ) N / 4 = T ( N / 8 ) N / 8 + 1 \frac{T(N/4)}{N/4}=\frac{T(N/8)}{N/8} + 1 N/4T(N/4)=N/8T(N/8)+1​​

T ( 2 ) 2 = T ( 1 ) 1 + 1 \frac{T(2)}{2}=\frac{T(1)}{1} + 1 2T(2)=1T(1)+1​​

l o g 2 n log_2n log2n个式子

将上面的式子相加可有

T ( N ) N = T ( 1 ) 1 + l o g N \frac{T(N)}{N} = \frac{T(1)}{1} + logN NT(N)=1T(1)+logN

得到 T ( N ) = N l o g N + N = O ( N l o g N ) T(N) = NlogN + N = O(NlogN) T(N)=NlogN+N=O(NlogN)

2、空间复杂度分析

归并排序需要O(N)的额外空间,也就是临时数组部分所占的空间

参考资料

  1. Acwing
  2. 数据结构与算法分析C语言描述(第二版) 7.6.1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值