[关闭]
@zimenglan 2015-01-20T15:19:30.000000Z 字数 2465 阅读 3240

剑指offer(09)-数组中的逆序对[分治]

逆序对 剑指offer


转载于Acm之家


题目来自剑指offer系列 九度 1348:

题目描述:

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
输入:
每个测试案例包括两行:
第一行包含一个整数n,表示数组中的元素个数。其中1 <= n <= 10^5。
第二行包含n个整数,每个数组均为int类型。
输出:
对应每个测试案例,输出一个整数,表示数组中的逆序对的总数。

样例输入:

4
7 5 6 4

样例输出:

5

思路一

直接的做法是逐个统计,复杂度是N^2,

思路二:

可以利用归并排序的思想,在排序过程中统计逆序对的个数。
时间复杂度依然是 N*Log(N)。 可以从代码中看到,只是比归并排序多了一句代码:cnt += (end-j+1);

注意:

由于最终个数可能超过int,这里用long long

  1. //============================================================================
  2. // Name : 求逆序对.cpp
  3. // Author : coder
  4. // Version :
  5. // Copyright : www.acmerblog.com
  6. // Description : Hello World in C++, Ansi-style
  7. //============================================================================
  8. #include <iostream>
  9. #include <stdio.h>
  10. using namespace std;
  11. typedef long long ll;
  12. int n, arr[100010], tmp[100010];
  13. //归并排序,过程中 统计逆序数
  14. ll merge(int start, int mid, int end){
  15. ll cnt = 0;
  16. int i = start, j = mid+1, k = start;
  17. while( i<=mid && j<= end){
  18. //从大到小排序
  19. if(arr[i] > arr[j]){
  20. cnt += (end-j+1); //右面剩下的都是逆序
  21. tmp[k++] = arr[i++];
  22. }else{
  23. tmp[k++] = arr[j++];
  24. }
  25. }
  26. while(i<=mid) tmp[k++] = arr[i++];
  27. while(j<=end) tmp[k++] = arr[j++];
  28. for(int i=start; i<=end; i++) arr[i] = tmp[i];
  29. return cnt;
  30. }
  31. ll inversePairs(int start, int end){
  32. ll cnt = 0;
  33. if(start < end){
  34. int mid = (start + end)/2;
  35. cnt += inversePairs(start, mid); //左半部分 逆序对数量
  36. cnt += inversePairs(mid+1, end); //右半部分
  37. cnt += merge(start, mid, end); //合并两部分,并计算数量
  38. }
  39. return cnt;
  40. }
  41. int main() {
  42. //freopen("in.txt", "r", stdin);
  43. while( scanf("%d", &n) != EOF){
  44. for(int i=0; i<n; i++) scanf("%d", &arr[i]);
  45. ll ans = inversePairs(0, n-1);
  46. printf("%lld\n",ans);
  47. }
  48. return 0;
  49. }
  50. //============================================================================
  51. // Name : 求逆序对.cpp
  52. // Author : coder
  53. // Version :
  54. // Copyright : www.acmerblog.com
  55. // Description : Hello World in C++, Ansi-style
  56. //============================================================================
  57. #include <iostream>
  58. #include <stdio.h>
  59. using namespace std;
  60. typedef long long ll;
  61. int n, arr[100010], tmp[100010];
  62. //归并排序,过程中 统计逆序数
  63. ll merge(int start, int mid, int end){
  64. ll cnt = 0;
  65. int i = start, j = mid+1, k = start;
  66. while( i<=mid && j<= end){
  67. //从大到小排序
  68. if(arr[i] > arr[j]){
  69. cnt += (end-j+1); //右面剩下的都是逆序
  70. tmp[k++] = arr[i++];
  71. }else{
  72. tmp[k++] = arr[j++];
  73. }
  74. }
  75. while(i<=mid) tmp[k++] = arr[i++];
  76. while(j<=end) tmp[k++] = arr[j++];
  77. for(int i=start; i<=end; i++) arr[i] = tmp[i];
  78. return cnt;
  79. }
  80. ll inversePairs(int start, int end){
  81. ll cnt = 0;
  82. if(start < end){
  83. int mid = (start + end)/2;
  84. cnt += inversePairs(start, mid); //左半部分 逆序对数量
  85. cnt += inversePairs(mid+1, end); //右半部分
  86. cnt += merge(start, mid, end); //合并两部分,并计算数量
  87. }
  88. return cnt;
  89. }
  90. int main() {
  91. //freopen("in.txt", "r", stdin);
  92. while( scanf("%d", &n) != EOF){
  93. for(int i=0; i<n; i++) scanf("%d", &arr[i]);
  94. ll ans = inversePairs(0, n-1);
  95. printf("%lld\n",ans);
  96. }
  97. return 0;
  98. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注