逆序数问题

给我们一个序列, 让我们求其逆序数:

如3 2 1 4

逆序数为: 2+1+0+0=3

 

我们这样定义一个序列的逆序数: 序列a1 a2 a3 a2 ...an

这个序列的逆序数C, 等于a1,a2...的逆序数的和.即 C=sum(Ci)

Ci为满足ai > aj (j > i)的数的总的个数, 即Ci = sum(ai > aj) (j >i).

 

我们一般写的算法一般会做N(N-1)/2次比较, 时间复杂度为: O(N^2).

 

下面采用的分而治之的思想来改进:

假设我们将序列a1 a2 a3 a2 ...an分成两份: B0=(a1 a2 an/2) B1 = (a (n/2+1)...an)

那么C=C(B0)+C(B1)+M(B0B1)

如果我们直接去计算M(B0B1), f(n) = 2*f(n/2)+c*n^2, 计算出来的结果是f(n)=n*f(1) + 2c*n^2 - 2c*n, 那么效率依然是O(N^2), 我们通过什么方式改进呢?

那假如让B0,B1有序就好了! 嗯,对的. 我们在归并排序的过程先将B0,B1排成有序数列,再来求B0′B1′的逆序数, 这时求M(B0′B1′)效率就是O(N).

即,C=C(B0′) + C(B1′) + M(B0′B1′).

下面给出求C(B0′B1′)的代码, 你在下面的完整的求逆序数的算法中也可以找到:

  1. int i = x, j = m; //序列B0[x,y], B1[m, n]   
  2. for(i = x; i <= y; ++i)  
  3. {  
  4.     while(j <= n && arr[i] > arr[j])  
  5.         ++j;  
  6.     nOrder += j-m;  
  7. }  
 

这时f(n) = 2*f(n/2)+c*n, 我计算出来的结果是f(n) = n*f(1) + c*n*log(n)

时间复杂度O(N*logN)和空间复杂度O(N)都和归并算法一致, 只比比归并算法大了一个常数因子.

 

  1. #include <iostream>  
  2. #include <algorithm>  
  3. using namespace std;  
  4. void swap(int *arr, int i, int j)  
  5. {  
  6.     int tmp = arr[i];  
  7.     arr[i] = arr[j];  
  8.     arr[j] = tmp;  
  9. }  
  10. int merge(int* temp, int *arr, int x, int y, int m, int n)  
  11. {  
  12.     int nOrder = 0;  
  13.     int i = x, j = m;  
  14.     for(i = x; i <= y; ++i)  
  15.     {  
  16.         while(j <= n && arr[i] > arr[j])  
  17.             ++j;  
  18.         nOrder += j-m;  
  19.     }  
  20.     int k = 0;  
  21.     i = x, j = m;  
  22.     while(i <= y && j <= n)  
  23.     {  
  24.         if(arr[i] <= arr[j])  
  25.             temp[k++] = arr[i++];  
  26.         else  
  27.             temp[k++] = arr[j++];  
  28.     }  
  29.     while(i <= y)  
  30.         temp[k++] = arr[i++];  
  31.     while(j <= n)  
  32.         temp[k++] = arr[j++];  
  33.     return nOrder;  
  34. }  
  35. int inversion_number(int *arr, int i, int j)  
  36. {  
  37.     if(i < j)  
  38.     {  
  39.         int mid = i+((j-i)>>1);  
  40.         int v1 = inversion_number(arr, i, mid);  
  41.         int v2  = inversion_number(arr, mid+1, j);  
  42.         int temp[10];  
  43.         int nValue = merge(temp, arr, i, mid, mid+1, j);  
  44.         memcpy(arr+i, temp, sizeof(int)*(j-i+1));  
  45.         return v1+v2+nValue;  
  46.     }  
  47.     else  
  48.         return 0;  
  49. }  
  50. int main()  
  51. {  
  52.     int arr[] = {5,3,3,3,3,3,3,3,3,3};  
  53.     cout << inversion_number(arr, 0, 9) << endl;  
  54.     return 0;  
  55. }  
 参考: http://blog.csdn.net/xiaofengsheng/article/details/5491090

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值