逆序對個數(歸併排序)

2021-10-05 16:19:41 字數 2089 閱讀 2512

description

給出乙個陣列a,問這個陣列中有多少個逆序對。

逆序對定義:若ia[j],則(a[i]​,a[j]​)是乙個逆序對。

如陣列3 4 1 2中的逆序對有(3,1),(3,2),(4,1),(4,2),共4個逆序對。

input

第一行乙個整數n,表示元素個數。

第二行n個空格分隔的整數a[i]。

output

乙個數,表示逆序對個數。

sample input 1

53 3 4 1 2

sample output 1

6hint

50%的資料 n ≤ 6000.

100%的資料 n ≤ 10^5,0 < a[i] < 2 ^31.

time limit

1000ms

memory limit

256mb

分析:由題目的資料範圍,知若是暴力地逐個搜尋逆序對,時間複雜度是o(n^2)的,一定會有超時的測試資料,就不能拿到所有的分數,所以得想時間複雜度更低的辦法。而眾多排序演算法中,歸併排序是一種天然的計算逆序對的利器,而且時間複雜度是o(nlogn),在本題不會超時。故要解答本題,只需在對資料實現歸併排序的時候,新增逆序對計算的操作就可以了。接下來簡單解釋為什麼歸併排序可以計算逆序對。

可以看出來,歸併排序實現時順便算逆序對非常便利,一次就可以算得一大打逆序對,乙個個地列舉判斷逆序對一次只能算得乙個逆序對,利用歸併排序的特性計算逆序對明顯提速。

#include

long

long

int n;

//序列長度

int a[

100001]=

;//儲存原始序列

int temp[

100001]=

;//實現歸併排序的中間道具

long

long

int ans=0;

//計數逆序對

//採用左閉右閉區間寫法,[left,right]

void

merge_sort

(long

long

int left,

long

long

int right)

long

long

int mid=left+

((right-left)

>>1)

;long

long

int i1,i2,cur;

merge_sort

(left,mid)

;//排左半

merge_sort

(mid+

1,right)

;//排右半

//道具準備

for(

long

long

int i=left;i<=right;i++

)//i1左半頭指標,i2右半頭指標

i1=left,i2=mid+1;

//[left,right]內進行排序,排序結果更新到原始序列a

for(cur=left;cur<=right;cur++

)else

if(i2>right)

else

if(temp[i1]

<=temp[i2]

)else

if(temp[i1]

>temp[i2])}

return;}

intmain()

merge_sort(1

,n);

printf

("%lld"

,ans)

;return0;

}

歸併排序易錯點:道具陣列和目標陣列搞混。

逆序對 (歸併排序)

逆序對的nlogn方法,改進後的歸併排序 給定排列p,求排列的逆序對數量。p的長度 100000。要求o nlogn 定義歸併排序過程merge l,r merge l,r merge l,mid merge mid 1,r count l,mid,mid 1,r 只需要考慮左右兩段之間造成的逆序對...

歸併排序 逆序對

按照劉汝佳說的,歸併排序分三步 1.劃分問題,即把序列分成元素盡量相等的兩半 2.遞迴求解 3.合併子問題 其實就是把乙個序列不斷的二分,直到只有兩個元素的時候,然後排序,然後返回,再排序。先上 include using namespace std long long a 100005 t 100...

歸併排序(逆序對)

現在我們在競賽中最常用的排序是快速排序,c 只要乙個sort就搞定,但非常明顯,歸併排序的時間複雜度是最優的而且非常穩定,但是人們經常把它用在求逆序對個數上面。那麼下面我用乙個這樣的題來講一下歸併排序。點這裡看題目和樹狀陣列解法。歸併排序是將數列a l,h 分成兩半a l,mid 和a mid 1,...