設\(A\)爲一個有\(n\)個數字的有序集\((n>1)\),其中全部數字各不相同。c++
若是存在正整數\(i,j\)使得\(1 ≤ i < j ≤ n\)並且\(A[i] > A[j]\),則\(<A[i], A[j]>\)這個有序對稱爲\(A\)的一個逆序對,也稱做逆序數。
——百度數組
咱們的冒泡排序的思想十分簡單,咱們每一次循環都將最大的數排到最後面。咱們每交換一次就是一個逆序對。優化
咱們冒泡排序有一個簡單的小優化:若是咱們某一輪沒有交換了,咱們的序列即爲有序。ui
for(int i=1;i<=n;i++) { bool flag=1; for(int j=1;j<=n-i;j++) if(a[j]>a[j+1]) swap(a[j],a[j+1]),flag=0,ans++; if(flag) break; }
時間複雜度\(O(n^2)\)。spa
咱們首先來了解一下歸併排序的思想:二分和合並。.net
三部曲:
1.劃分:把序列分紅元素個數儘可能相等的兩半
2.遞歸求解:把兩半元素分別排序
3.合併問題:把兩個有序表合併爲一個code
合併:每次只需把左右兩邊序列的最小元素進行比較,把其中較小的元素加入到合併後的輔助數組中。blog
void msort(int s,int t) { if(s==t) return ; int mid=(s+t)>>1; msort(s,mid),msort(mid+1,t); int i=s,j=mid+1,k=s; while(i<=mid && j<=t) { if(a[i]<=a[j]) r[k]=a[i],k++,i++; else r[k]=a[j],k++,j++; } while(i<=mid) r[k]=a[i],k++,i++; while(j<=t) r[k]=a[j],k++,j++; for(int i=s;i<=t;i++) a[i]=r[i]; return ; }
咱們能夠假設左邊序列爲\({3,4,7,9}\),右邊爲\({1,5,8,10}\)排序
咱們的第一步就是比較\(1\)和\(3\),由於\(1<3\),因此\(1\)入預備數組。遞歸
咱們這時候能夠發現,咱們的\(1\)與左邊序列的\(3\)和\(3\)以後的數都是逆序對,一共就\(4\)對了。這也是歸併排序找逆序對快的緣由。
咱們只須要在\(a[i]>a[j]\)時,答案加上\(mid-i+1\)。
void msort(int s,int t) { if(s==t) return ; int mid=(s+t)>>1; msort(s,mid),msort(mid+1,t); int i=s,j=mid+1,k=s; while(i<=mid && j<=t) { if(a[i]<=a[j]) r[k]=a[i],k++,i++; else r[k]=a[j],k++,j++,ans+=mid-i+1; } while(i<=mid) r[k]=a[i],k++,i++; while(j<=t) r[k]=a[j],k++,j++; for(int i=s;i<=t;i++) a[i]=r[i]; return ; }
時間複雜度\(O(n\ log\ n)\)。
樹狀數組
咱們的樹狀數組也是能夠求逆序對的。
實際上就是統計當前元素的前面有幾個比它大的元素的個數,而後把全部元素比它大的元素總數累加就是逆序對總數。
#include <bits/stdc++.h> using namespace std; int n,a[1005],lsh[1005],BIT[1005]; int lowbit(int x) { return x & -x; } void update(int k,int x) { for(int i=k;i<=n;i+=lowbit(i)) BIT[i]+=x; return ; } int ask(int x) { int ans=0; for(int i=x;i;i-=lowbit(i)) ans+=BIT[i]; return ans; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]),lsh[i]=a[i]; sort(lsh+1,lsh+n+1);//離散化 unique(lsh+1,lsh+n+1)-lsh-1; for(int i=1;i<=n;i++) a[i]=lower_bound(lsh+1,lsh+n+1,a[i])-lsh; int ans=0; for(int i=1;i<=n;i++) { update(a[i],1);//在a[i]這個位置上加1 ans+=i-ask(a[i]); } printf("%d",ans); return 0; }
時間複雜度\(O(n\ log\ n)\)。
逆序對能夠表示成一個數前面有幾個比這個數大的數,就表示這個數所造成的逆序對數。
咱們查找就是:
找\(a[i]+1\)~\(Max\)的值。
咱們加入就是:
\(a[i]\)這個位置的數量加\(1\)。
咱們最後把查找到的全部對數輸出便可。
但咱們的數可能很大,咱們這時就須要使用到離散化了。
#include <bits/stdc++.h> using namespace std; int n,a[500005],lsh[500005],maxx; struct SementTree{ int l,r,sum; }t[2000005]; void read(int &x) { int f=1; x=0; char ch=getchar(); while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } x*=f; } void init() { sort(lsh+1,lsh+n+1); unique(lsh+1,lsh+n+1)-lsh-1; for(int i=1;i<=n;i++) a[i]=lower_bound(lsh+1,lsh+n+1,a[i])-lsh; return ; } void build(int p,int l,int r) { t[p].l=l,t[p].r=r,t[p].sum=0; if(l==r) { return ; } int mid=(l+r)>>1; build(p<<1,l,mid),build(p<<1|1,mid+1,r); return ; } void update(int p,int u) { if(t[p].l==t[p].r) { t[p].sum++; return ; } int mid=(t[p].l+t[p].r)>>1; if(u<=mid) update(p<<1,u); else update(p<<1|1,u); t[p].sum=t[p<<1].sum+t[p<<1|1].sum; return ; } int ask(int p,int l,int r,int ll,int rr) { if(ll<=l && r<=rr) return t[p].sum; int mid=(l+r)>>1; int ans=0; if(ll<=mid) ans+=ask(p<<1,l,mid,ll,rr); if(mid<rr) ans+=ask(p<<1|1,mid+1,r,ll,rr); return ans; } int main() { read(n); for(int i=1;i<=n;i++) read(a[i]),lsh[i]=a[i],maxx=max(maxx,a[i]); init(); build(1,0,maxx); int ans=0; for(int i=1;i<=n;i++) { ans+=ask(1,0,maxx,a[i]+1,maxx); update(1,a[i]); } printf("%d",ans); return 0; }