归并排序
** 写在前面的话: 归并排序的时间复杂度为O(N*logN)
,额外空间复杂度为O(N)
**.
什么是归并排序呢?
- 一点点专业叫法: 迭代就理解为递归,分治就理解为一分为二的过程.
- 举例:以[2,5,7,1,6,4]为例
- 首先将其一分为二,划分成两个部分,分别为
[2,5,7]
和[1,6,4]
. - 然后分别对左右两部分进行排序,得到
[2,5,7]
和[1,4,6]
. - 申请一个辅助数组,长度和原来的数组相同.
- 左右两个部分分别从左到右进行比较,谁小就放到辅助数组中去,例如此例中,执行顺序是这样的
- 先
2
和1
比较,2
大,就把1
放在辅助空间中,然后左边数组下标不动,右边下标+1. - 再
2
和4
比较,4
大,就把2
放进辅助空间,然后左边数组下标+1,右边数组下标不变. - …以此类推,当一个到了末尾之后,比较停止,剩下的直接全部放到辅助数组中.
- 最后得到有序数组
[1,2,4,5,6,7]
,再将其复制回原数组即可.
- 先
- 首先将其一分为二,划分成两个部分,分别为
分析其时间复杂度
* 这是一个递归的过程,我们可以用`master公式(): T(N) = a * T(N / b) + O(N^d)`来套一下.
* 假设数组样本量为`N`,划分成了两部分,所以两边的样本量是`(N/2)`,有因为有两部分,所以为`2*T(N/2)`.
* 额外空间复杂度为`O(N)`,因为要申请一个长度为N的辅助数组.
* 用`master`公式套,可得`T(N) = 2 * T(N / 2) + O(N)`,即`a=2,b=2,d=1`.
* 由此可知,`(log(b,a)) => 1`,而`d == 1`,所以可得归并排序的时间复杂度为***`O(N^d * logN) == O(N*log(N))`***.
* 对于额外空间复杂度,就把它当作只生成了一个最大的数组(就是初始化数组的长度),不考虑中间的就好了,所以是`O(N)`.
代码:
- 行数有点多,我就直接放github上了,需要的话就自己看下: 归并排序
举例:
-
小和问题
(以[1,3,4,2,5]为例)
- 什么是小和问题: 就是在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和,求一个数组的小和的和.
- 分析:
- 1左边比1小的数: 没得.
- 3左边比3小的数: 1.
- 4左边比4小的数: 1,3.
- 2左边比2小的数: 1.
- 5左边比5小的数: 1,3,4,2.
- 所以总的小和为:1+1+3+1+1+3+4+2 = 16.
- 解决办法
- 每个数都遍历一下左边,小的数就相加,其时间复杂度为
O(N^2)
,我们对数器就可以这么写. - 用归并思想,产生小和就在合并过程中产生,但是其中有一个要注意的点:
- 在合并的时候,假如左边为
[1,3,4]
,右边为[2,5]
. - 先
1
和2
进行比较,发现2 > 1
,***而此时,2
右边包含2
本身一共有两个
元素,所以此时需要将1×2
***,然后在将右边的1
放到辅助数组中. - 再将
3
和2
比较,发现3 > 2
,此时不产生小和,直接将2
放入辅助数组. - …以此类推.
- 其实就是,合并的时候左边比右边大,就产生小和,左边比右边小就不产生小和,同时要注意右边的数比左边大的时候,要看
右边较大的这个数的右边还有几个数
,此时左边较小的数就要乘以几
. - 时间复杂度和归并排序一致,为
O(N*log(N))
.
- 在合并的时候,假如左边为
- 每个数都遍历一下左边,小的数就相加,其时间复杂度为
- 代码超链接: 小和问题代码.
-
逆序对问题
(以[1,3,4,2,5]为例)
- 逆序对就是指在一个数组中,左边的数比右边的数大,则这两个数构成一个逆序对,请打印所有的逆序对.
- 思路与小和问题相同,我就不多赘述了哈,就是在归并重组的时候找出逆序对,放在一个list中返回就可以了.
- 直接来代码吧哈哈.逆序对问题代码.
-
偶然发现的一个牛客剑指offer上的题目,就是一个归并的题目.
- 题目地址: 逆序对问题, 我的代码上面也有哈哈哈,大家可以参考,就是一个归并,不过多了一个取模操作而已.