详解归并排序及其应用

912. 排序数组

315. 计算右侧小于当前元素的个数

493. 翻转对

327. 区间和的个数

 

排序算法千千万,今天先讲归并!!

算法思想

「归并排序」其实是一种分而治之的思想。需要把 10 个数字排好序,先分一半,各自排好后,然后合并。简而言之:先分后治

5

看了这个图,是不是感觉就是一棵树的后续遍历过程,详情可见 二叉树--纲领篇

现在来分析一下「归并排序」的时间复杂度

对于长度为n的数组来说,在「分」的过程中,会构建出如上图所示的一棵树,其高度为logN,每一层均有n个节点

在「治」的过程中,每一层就是合并两个有序数组,其时间复杂度为o(N)

所以最终时间复杂度为o(nlogn)

算法框架

算法应用

最简单的应用肯定就是给一个数组,然后排序。如题目 排序数组

今天来介绍几个难「亿」点的题目

计算右侧小于当前元素的个数

题目详情可见 计算右侧小于当前元素的个数

这个题目可以直接用暴力,但是想都不用想,绝对超时!!

我们可以利用「归并排序」的特性,在「合并」的时候,两边各自有序,且相对位置固定

我们抽取一个「治」的子过程:

6

可以看到两边各自有序,且[2,5,6,7]一定处于原数组下标为0-3的位置,而[1,3,4,9]一定处于原数组下标为4-7的位置

所以[2,5,6,7]右边的元素就是[1,3,4,9]

7

假设此时处于上图所示的时刻,即:1,2,3,4已经排好序,i处于5的位置,j处于9的位置

由于5 < 9,所以i需要向前移动一格,同时,我们可以知道右边比5小的元素有3个,即:1,3,4

总结一下:每次i向前移动一格,就可以知道右边比i位置小的元素个数

现在还有另外一个问题,在移动的过程中,每个元素的原下标早已改变,比如元素5原来处于下标0的位置,现在变到了1的位置

所以,我们需要定一个一个新的数据结构,记录每个元素和原下标的对应关系

下面请看详细代码:

翻转对

题目详情可见 翻转对

6

我们可以维护一个区间:

可以知道对于满足6的区间,肯定也满足7。所以每次判断下一个数时,只需要进行区间扩张即可!!

下面请看详细代码:

区间和的个数

题目详情可见 区间和的个数

一开始看到这个题目,下意识的想用「滑动窗口」,滑动窗口相关文章可见 滑动窗口技巧子数组之滑动窗口篇

可是可是可是...这里不能用「滑动窗口」,为什么呢?因为滑动窗口必须具有单调性,这个题目一会「➕」一会「➖」

由于题目需要求在[lower, upper]范围内的所有子数组数量,所以可以把问题转化成对「前缀和」归并排序的过程中计算满足要求数量

有人可能会问,前缀和不就是递增的嘛?还需要排啥序呀!!不不不,仔细看这个题目中有正有负,所以前缀和不一定递增

6

我们可以维护一个区间:对于的一个元素x,先找到右边的下界,然后找到右边的上届,假设满足区间为[a, b]

对于下一个元素y,一定满足x <= y且右边的下界一定>= a,右边的上界一定>= b

根据上述特点,给出详细代码: