分而治之:逆序对计数问题
问题:
输入一个长度长度为n的数组A[n]
,求出数组A[n]
逆序对的总数。
输入:
长度为n的数组A[n]
输出:
数组A[n]逆序对的总数
问题分析:
- 把数组A二分为两个子数组A[1…n/2],A[n/2 + 1…n]
- 递归求解子问题
求解S1∶仅在A[1…n/2]中的逆序对数目
求解S2∶仅在A[n/2+1…n]中的逆序对数目 - 合并A[1…n/2]和A[n/2 +1…n]的解
求解S3∶跨越子数组的逆序对数目
S = S1+S2+S3
与最大子数组的问题一样,执行效率的瓶颈值也在合并方面,也就是S3跨越子数组的逆序对数目的求解问题。
S3的求解:
1. 策略一:直接求解
(1)对每个Aj]∈ A[m +1…n],枚举A[i]∈ A[1…m]并统计逆序对数目
(2)求解S的算法运行时间:O(n2)
直接求解的运行时间不是很理想,所以,有没有更好的方法呢?
2. 策略二:排序求解
(1)分别对数组A[1…m]和A[m+ 1…n]进行排序;
(2)对于每个A[ j ]∈A[m+1…n],采用二分查找为其在A[1…m]中定位;
(3)A[ j ]在A[1…m]定位点右侧的元素均可与A[ j ]构成逆序对;
(4)求解S的算法运行时间:O(nlogn)。
排序求解S的分而治之提高了算法运行时间,但仍然还有优化的可能;
3. 策略三:归并求解
(1)从左到右扫描有序子数组:A[i]∈A[1…m],A[j] ∈ A[m+1…n]
···如果A[i] > A[j],统计逆序对,j向右移
···如果A[i] ≤ A[j],i向右移
(2)利用归并排序框架保证合并后数组的有序性
(3)S时间复杂度降至O(n)
算法分析图:
代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 10000;
int a[N],b[N];
int count;
void Merge(int a[], int l, int mid , int r) //归并排序的合并部分
{
int i = l, j = mid + 1,k = l;
//遍历统计逆序数对
while(i