您的位置:首页 > 其它

分治算法题:nlgn时间复杂度计算原序列的重要逆序个数

2017-04-16 23:38 281 查看
题目描述:

Recall the problem of finding the number of inversions. As in the text, we are given a sequence of n numbers a1, ... , an, which we assume are all distinct, and we define an inversion to be a pair i< j such that ai>aj.  

We motivated the problem of counting inversions as a good measure of how different two orderings are. However, one might feel that this measure is too sensitive. Let’s call a pair a significant inversion if i< j and ai> 2aj. Given an O(nlogn) algorithm to count
the number of significant inversions between two orderings.

我的理解:让我们求一个序列的significant inversion个数,这个不同于一般的逆序数,但是求的思想是一样的,都是采用分治法和归并排序的算法实现。但是不同于普通的求逆序数算法,如果是求ai>aj的个数的话,可以在计算合并子序列的逆序数个数的过程中顺便排序。因为如果不满足ai>aj的话,那就将aj放入tmp序列中,继续比较ai和a(j+1)的大小。

在思考多久之后,决定在函数merge的实现中,将计算合并的过程中产生的重要逆序数和给这个合并子序列排序分开用两个while循环实现。具体实现看代码。

#include<iostream>
#include<stdio.h>
#include<cmath>
#include<string>
#include<string.h>
#include<set>
#include<map>
#include <algorithm>
using namespace std;

/*使用分治法和归并排序的思想来算逆序数,nlgn复杂度,同时完成了对原序列的排序*/
//合并A[left]-A[mid]和A[mid+1]-A[right]
int merge(int A[], int left, int mid, int right) {
int *tmp = new int[right-left+1];
int ret = 0;
int i = left, j = mid + 1,cnt = 0;
int k = mid - left + 1;
/*计算满足题意的逆序数个数*/
while (i <= mid && j <= right) {
if(A[i] > 2 * A[j]) {
ret += k;
j++;
}
else{
k--;
i++;
}
}
/*给子序列合并排序*/
i = left, j = mid + 1;
while (i <= mid && j <= right) {
if(A[i] > A[j]) {
tmp[cnt++] = A[j];
j++;
}
else{
tmp[cnt++] = A[i];
i++;
}
}
while (i <= mid) {
tmp[cnt++] = A[i];
i++;
}
while (j <= right) {
tmp[cnt++] = A[j];
j++;
}
//至此,tmp为合并后排好序的序列 ,接下来将tmp的值复制给A
for (k = 0; k < right - left + 1; k++) {
A[left + k] = tmp[k];
}
delete []tmp;
return ret;
}

int inversionCount(int A[], int left, int right) {
if(left >= right) return 0;
int mid = (left + right) / 2;
int num1 = inversionCount(A, left, mid);
int num2 = inversionCount(A, mid+1, right);
return num1+num2+merge(A, left, mid, right);
}

int main() {
int n, num[100];
while(scanf("%d", &n) == 1) {
for (int i = 0; i < n; i++) scanf("%d", num + i);
int res = inversionCount(num, 0, n - 1);
/*输出重要逆序数个数*/
printf("%d\n", res);
/*遍历输出排好序的序列*/
for (int i = 0; i < n-1; i++) printf("%d ", num[i]);
printf("%d\n", num[n-1]);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐