题目1:有一个数的出现次数严格大于n/2;题目2:逆序对个数

归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)。 

    下面为方法四的代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int a[maxn], t[maxn];
long long cnt;
//oid prt(int n) {
// for(int i = 1; i <= n; i++) printf("%d ", a[i]);
// printf("\n");
//}
void solve(int l, int r) {

if(l >= r) return;

int mid = (l + r) >> 1;

solve(l, mid);//分治左边

solve(mid + 1, r);//分治右边 // printf("solve(%d, %d)\n", l, r); // prt(5);// printf("cnt=%d\n", cnt);

//分治过程中已经排好序,用两个指针扫描一遍两边

for(int i = mid, j = r; i >= l; i--) {

while(a[i] <= a[j] && j > mid) j--;

if(j - mid == 0) break;

cnt += j - mid;//这一步是在统计跨两侧的逆序对个数,这步的操作基础是一左一右两个已经排好序的子序
 //列

}// printf("cnt=%d\n", cnt);

//排序,类似归并排序,两个指针扫描两边

for(int i = l; i <= mid; i++) t[i] = a[i];//复制

for(int i = l, j = mid + 1; ; ) {//t代表左侧序列,a中存右侧序列和合并后序列,这一步是排序合
                                 //并

if(t[i] > a[j]) a[i+j-mid-1] = a[j++];//把两个指针较小的一个放到开始

else a[i+j-mid-1] = t[i++];

if(i > mid) break;//左侧的已经并入a,不用再进行其他操作,因为右侧数字本来就保存在a中

if(j > r) {//右侧的已经安排完毕,此时需要把t中左侧的数字全部安排进入a中

while(i <= mid) a[i+j-mid-1] = t[i++];

break;

}

}

}
int main() {

int T; scanf("%d", &T);

while(T--) {

int n; scanf("%d", &n);

for(int i = 1; i <= n; i++) scanf("%d", &a[i]);

cnt = 0;

solve(1, n);

printf("%lld\n", cnt);

}

return 0;

}

猜你喜欢

转载自blog.csdn.net/huaiyingdetective/article/details/82829445