求解逆序对数的几种方法
2016-03-03 16:04
239 查看
求解逆序对数的几种方法:
设A为一个包含n个数字的有序集合(n>1),其中所有的数字均不相同。如果存在两个正整数i,j,使得1≤i<j≤n,且A[i]>A[j],则(A[i],A[j])这个有序对称为A的一个逆序对。例如,数组(3,1,4,5,2)的逆序对有(3,1),(3,2),(4,2),(5,2),共4个。
1. 两重循环来求解,时间复杂度为O(n2),大致代码如下:
int GetReverse(int* a, int n) { int sum = 0; for (int i = 0; i < n-1; ++i) { for (int j = i+1; j < n; ++j) { if (a[i] > a[j]) ++sum; } } return sum; }
2. 树状数组求解,时间复杂度O(nlogn)
大致思想:假设我们有个标记数组flag,长度为n;
先在n个数中找到最大的元素e1,其下表为p1,将flag[p1]置为1,统计p1之前的1的个数,即为与e1构成的逆序对数;
找到第二大元素e2,其下标为p2,将flag[p2]置为1,统计p2之前的1的个数,即为与e2构成的逆序对数;
以此类推,全部找完之后,统计的总个数即为序列中逆序对的总数。
拿开头的例子来说:
3 1 4 5 2,初始状态flag数组均为0(约定下标从1开始);
取最大数5,flag数组变为 0 0 0 1 0,统计下标4之前的1的个数为0,总逆序对数为0;
取第二大数4,flag数组变为 0 0 1 1 0,统计下标3之前的1的个数为0,总逆序对数为0;
取第三大数3, flag数组变为 1 0 1 1 0,统计下标1之前的1的个数为0,总逆序对数为0;
取2,flag数组变为 1 0 1 1 1,统计下标5之前的1的个数为3,总逆序对数为3;
取1,flag数组变为 1 1 1 1 1,统计下标2之前的1的个数为1,总逆序对数为4;
结束。
为了降低找第k大数的时间复杂度,我们先用快排对n个数进行降序排列,用树状数组进行求和统计。
为了记录第k大数的位置,我们用结构体来储存数据值和下标。
示例代码如下:
// 数据输入 for (int i = 1; i <= n; ++i) { scanf("%d", &num[i].val); num[i].id = i; } // 降序排列 sort(num+1, num+1+n, cmp); // 找到第k大数,统计它的下标之前总共1的个数,加到ans当中,最终输出ans即为结果 // 标记第k大数的位置,并向上更新树状数组,两个操作顺序不能颠倒 for (int i = 1; i <= n; ++i) { ans += GetSum(num[i].id); Update(num[i].id); }
3归并排序求解:
未完待续……相关文章推荐
- 逆序对
- 插入排序移动次数
- 归并排序-逆序对的求解
- poj_2299
- 【转载】高效排序——归并排序
- 逆序对的实现,利用逆序对特性
- POJ - 2299 Ultra-QuickSort【归并排序】
- 【SGU 180】Inversions —— 归并排序或树形数组计算逆序对
- 逆序对的递归求法
- 数组中的逆序对(面试题 36)
- 归并排序之逆序对
- 归并排序顺带求逆序对
- NOIP2013提高组 B.火柴排队 (逆序对)
- POJ 2299 Ultra-QuickSort(求逆序对)
- 【日常学习】【归并逆序对】codevs1688 求逆序对题解
- 【基础练习】【归并逆序对】codevs3324 新斯诺克题解
- 【基础练习】【离散化+逆序对】codevs3286 火柴排队题解
- 归并排序求逆序对
- 【剑指Offer面试编程题】题目1348:数组中的逆序对--九度OJ
- 【POI2012】【BZOJ2789】Letters