问题描述
定义:逆序对:
在 int 数组 array 中,i < j && array[i] > array[j] 则为一个逆序对
input:输入一个 int 数组,返回数组中的逆序对个数
算法简介
一、暴力枚举法
毫无技巧,两层循环遍历,时间复杂度为 O(n^2)
二、分而治之法
时间复杂度为 O(nlogn)
二分拆分原数组,算出左边数组的逆序对个数
s
1
s_1
s1,右边数组的逆序对个数
s
2
s_2
s2 ,因合并而产生的逆序对个数
s
3
s_3
s3,则原数组的逆序对个数:
s
=
s
1
+
s
2
+
s
3
s = s_1 + s_2 + s_3
s=s1+s2+s3
1、 s 1 s 2 s_1 \space s_2 s1 s2 计算函数
int CountInver(int left, int right, int array[])
:
用于递归二分计算 s 1 s 2 s_1\space s_2 s1 s2 ,用 left、right、mid 区分左右数组的开始和结尾
当 left == right 的时候返回
2、 s 3 s_3 s3 计算函数
int MergeCount(int left, int mid, int right, int input[])
:
用于计算 s 3 s_3 s3,若采用朴素的比较法:两层循环遍历,则时间复杂度不减反增
想法:若 array[left, mid] 和 array[mid + 1, right] 都是有序的,则合并寻找逆序对的复杂度仅为 O(n) ,而想让左右数组都是有序的也是可以简单实现的,类似于二分排序的思想
该算法关键在于 s 3 s_3 s3 求解的优化——利用二分排序思想
代码实现
// 此程序解决逆序对计数问题
// 逆序对:在int数组Array中,i<j && Array[i] > Array[j] 则为一个逆序对
#include <iostream>
#include <cstdio>
using namespace std;
int CountInver(int left, int right, int array[]);
int MergeCount(int left, int mid, int right, int input[]);
int main(void) {
int Array[100] = {0}; // 原数组, 简单起见不要超过100
int capacity; // 数组容量
cout << "输入数组的容量:";
cin >> capacity;
for(int i=0; i < capacity; i++) cin >> Array[i];
// 暴力枚举法
// 时间复杂度 O(n^2)
int InverTimes1 = 0;
for(int i=0;i<capacity;i++) {
for(int j=i+1;j<capacity;j++) {
if(Array[i] > Array[j]) InverTimes1++;
}
}
cout << InverTimes1 << endl;
// 分而治之法
// 拆分原数组,算出左半部分逆序对个数s1,右半部分逆序对个数s2,合并起来的逆序对个数s3,相加为总逆序对个数
// 重点在于优化s3计算算法,采用二路归并排序使其成为有序数组,在合并过程中得到s3
// 时间复杂度 O(nlogn)
int InverTimes2;
InverTimes2 = CountInver(0, capacity - 1, Array);
cout << InverTimes2 << endl;
return 0;
}
int MergeCount(int left, int mid, int right, int input[]) {
int tool[right - left + 1], i = 0; // tool as the cache
int loc1 = left; // loc1 as the begin of left array
int loc2 = mid + 1; // loc2 as the begin of right array
int s3 = 0; // add s3 in the MergeCount
while(i < right - left + 1) {
if(input[loc1] < input[loc2])
tool[i++] = input[loc1++];
else {
tool[i++] = input[loc2++];
s3 += mid - loc1 + 1; // find inverse times
}
if(loc1 > mid) {
while(loc2 <= right)
tool[i++] = input[loc2++];
break;
} else if(loc2 > right) {
while(loc1 <= mid)
tool[i++] = input[loc1++];
break;
}
}
for(int i=0;i<right - left + 1;i++) // put tool[] into the input[]
input[i+left] = tool[i];
return s3;
}
int CountInver(int left, int right, int array[]) {
if(left == right) return 0;
else {
int s1, s2, s3;
int mid = (left + right) / 2;
s1 = CountInver(left, mid, array);
s2 = CountInver(mid + 1, right, array);
s3 = MergeCount(left, mid, right, array);
return s1 + s2 + s3;
}
}