关于逆序对的两种做法(归并排序+树状数组c++)
来源:互联网 发布:阿里巴巴淘宝城 编辑:程序博客网 时间:2024/05/16 14:55
这些天来测试的时候遇到了几次逆序对的题目,但没有太在意,已经跪了好几次了,所以今天打算学习总结一下逆序对的两种做法。
首先是归并排序
归并排序的模板
#include<iostream>using namespace std;int a[100000],temp[100000];void merge_i(int l,int mid,int r){int i=l,j=mid+1,k=l;//i是从l到mid枚举,j从mid+1到r枚举加入中介数组while(i<=mid&&j<=r){if (a[i]>a[j])temp[k++]=a[j++];elsetemp[k++]=a[i++];//按从小到大的顺序加入中介数组}while(i<=mid) temp[k++]=a[i++];//把左边区间剩余的加入中介数组while(j<=r) temp[k++]=a[j++];//把右边去见剩余的加入中介数组for(i=l;i<=r;i++)a[i]=temp[i];//返回排列顺序}void merge(int l,int r){if (l<r)//这个很重要 不然会运行错误{int mid=(l+r)>>1;//找中间位置merge(l,mid);//左区间排列merge(mid+1,r);//右区间排列merge_i(l,mid,r);//总的排列 }}int main(){int n;cin>>n;int i;for(i=1;i<=n;i++)cin>>a[i];merge(1,n);for(i=1;i<=n;i++) cout<<a[i]<<" ";return 0;}
接下来是关于归并排序对于逆序对的作用
转载于:http://blog.csdn.net/acdreamers/article/details/16849761(今天看的感觉还好理解,感谢ACdreamers大大)
我们可以这样考虑:
归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。
在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在
前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并
排序中的合并过程中计算逆序数.
接下来上代码
#include<iostream>using namespace std;int a[100000],temp[100000];long long ans=0;void merge_i(int l,int mid,int r){int i=l,j=mid+1,k=l;while(i<=mid&&j<=r){if (a[i]>a[j]){temp[k++]=a[j++]; ans+=mid-i+1;}elsetemp[k++]=a[i++];}while(i<=mid) temp[k++]=a[i++];while(j<=r) temp[k++]=a[j++];for(i=l;i<=r;i++)a[i]=temp[i];}void merge(int l,int r){if (l<r){int mid=(l+r)>>1;merge(l,mid);merge(mid+1,r);merge_i(l,mid,r); }}int main(){int n;cin>>n;int i;for(i=1;i<=n;i++)cin>>a[i];merge(1,n);for(i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;cout<<ans;return 0;}
接下来我们总结树状数组求逆序对
转载于http://blog.csdn.net/alongela/article/details/8142965(感谢Onlyan大大)
给定n个数,要求这些数构成的逆序对的个数。除了用归并排序来求逆序对个数,还可以使用树状数组来求解。
树状数组求解的思路:开一个能大小为这些数的最大值的树状数组。从头到尾读入这些数,每读入一个数就更新树状数组,查看它前面比它小的已出现过的有多少个数sum,然后用当前位置减去该sum,就可以得到当前数导致的逆序对数了。把所有的加起来就是总的逆序对数。
题目中的数都是独一无二的,这些数最大值不超过999999999,但n最大只是500000。如果采用上面的思想,必然会导致空间的巨大浪费,而且由于内存的限制,我们也不可能开辟这么大的数组。因此可以采用一种称为“离散化”的方式,把原始的数映射为1-n一共n个数,这样就只需要500000个int类型的空间。
离散化的方式:
struct Node
{
int val;
int pos;
};
Node node[500005];
int reflect[500005];
val存放原数组的元素,pos存放原始位置,即node[i].pos = i。
把这些结构体按照val的大小排序。
reflect数组存放离散化后的值,即reflect[node[i].pos] = i。
这样从头到尾读入reflect数组中的元素,即可以保持原来的大小关系,又可以节省大部分空间。
上代码
#include<iostream>#include<cstdio>#include<algorithm>using namespace std;struct tree{int v,x;}node[100000];int n,a[100000],c[100000],reflect[100000];bool cmp(tree a,tree b){return a.v<b.v;}int lowbit(int i){return i&(-i);//假如i是奇数,返回1,否则返回i的因数当中2的最大次方 例如 lowbit(8)返回8,lowbit(6)返回2 lowbit(20)返回4}void update(int i)//更新树状数组{while(i<=n){c[i]++;i+=lowbit(i);}}int sum(int i)//查看它前面比它小的已出现过的有多少个数sum{int t=0;while(i>0){t+=c[i];i-=lowbit(i);}return t;}int main(){int i;long long ans=0; cin>>n;for(i=1;i<=n;i++) {cin>>node[i].v;node[i].x=i;//记录输入位置}sort(node+1,node+1+n,cmp);//排序for(i=1;i<=n;i++) reflect[node[i].x]=i;//离散化,优化空间,并记录当前位置for(i=1;i<=n;i++){update(reflect[i]);ans+=i-sum(reflect[i]);//求逆序对的个数}cout<<ans;return 0;}
- 关于逆序对的两种做法(归并排序+树状数组c++)
- 逆序对的两种算法【树状数组 / 归并排序】
- 归并排序,树状数组 两种方法求逆序对
- poj 2299(逆序对(树状数组||归并排序))
- ural1090 逆序对(归并排序和树状数组)
- 逆序对(树状数组/归并)
- 【算法】逆序对问题的四种解法(归并排序,BST,树状数组,线段树)及变形
- 内排序- 逆序对问题 树状数组& 归并排序写法
- poj 2299 求逆序对 树状数组 归并排序
- Inversion (hdu 4911 树状数组 || 归并排序 求逆序对)
- 2299 求逆序数对 归并排序/树状数组
- 归并排序之数组的逆序对
- 数组中的逆序对(归并排序)
- 1348:数组中的逆序对(树状数组 or 归并)
- Ultra-QuickSort (poj 2299 归并排序 || 树状数组 求逆序对)
- 求逆序对模板题(完善模板:树状数组或归并排序)
- poj_2299 Ultra-QuickSort(归并排序/树状数组 求逆序对)
- Noip2013 Day1 T2 火柴排队(归并排序/树状数组 求逆序对)
- Oracle_函数
- hibernate.cxf.xml 配置文件 解决 内容必须匹配 "(property*,mapping*,(classcachecollectioncache)*,event*,listener*)
- 【BZOJ】4614 [Wf2016] Oil
- ios 手动引入第三方库
- 求最长回文串(Manacher算法)
- 关于逆序对的两种做法(归并排序+树状数组c++)
- vs2013 编译 snmp++-3.3.7
- 20161027
- 转载特殊函数
- igb uio小结
- Python编程入门-第六章 字符串 -学习笔记
- Android Input之JoyStick
- struts2拦截器
- WPF CM框架下点击不同的按钮切换到不同的界面