求逆序數的幾種方法

2021-06-22 22:47:37 字數 3141 閱讀 8012

求乙個數列的逆序數

逆序對:數列a[1],a[2],a[3]…中的任意兩個數a[i],a[j] (i,如果a[i]>a[j],那麼我們就說這兩個數構成了乙個逆序對

逆序數:乙個數列中逆序對的總數

如數列3 5 4 8 2 6 9

(5,4)是乙個逆序對,同樣還有(3,2),(5,2),(4,2)等等

那麼如何求得乙個數列的逆序數呢?

方法1:乙個乙個的數

最簡單也是最容易想到的方法就是,對於數列中的每乙個數a[i],遍歷數列中的數a[j](其中j若a[i]則逆序數加1,這樣就能統計出該數列的逆序數總和

該方法的時間複雜度為o(n^2),具體過程就不細說了

方法2:歸併的思想

有一種排序的方法是歸併排序,歸併排序的主要思想是將整個序列分成兩部分,分別遞迴將這兩部分排好序之後,再和並為乙個有序的序列,核心**如下

mergesort(first,last)

在合併的過程中是將兩個相鄰並且有序的序列合併成乙個有序序列,如以下兩個有序序列

seq1:3  4  5

seq2:2  6  8  9

合併成乙個有序序:

seq:2  3  4  5  6  8  9

對於序列seq1中的某個數a[i],序列seq2中的某個數a[j],如果a[i]沒有逆序數,如果a[i]>a[j],那麼逆序數為seq1中a[i]後邊元素的個數(包括a[i]),即len1-i+1,

這樣累加每次遞迴過程的逆序數,在完成整個遞迴過程之後,最後的累加和就是逆序的總數

實現**如下:

[cpp]view plain

copy

const

intlength=100;  

inttemp[length];  

//額外的輔助陣列

intcount=0;  

void

merge(

int* array,

intfirst,

intmed,

intlast)  

else

}  while

(i<=med)  

while

(j<=last)  

for(int

m=0;m

}  void

mergesort(

int*array,

intfirst,

intlast)  

intmed=first+(last-first)/2;  

mergesort(array,first,med);  

mergesort(array,med+1,last);  

merge(array,first,med,last);  

}  

歸併排序的複雜度為o(nlogn),當然此方法的複雜度也為o(nlogn)

方法3:用樹狀陣列

還是以剛才的序列

3  5  4  8  2  6  9

大體思路為:新建乙個陣列,將陣列中每個元素置0

0  0  0  0  0  0  0

取數列中最大的元素,將該元素所在位置置1

0  0  0  0  0  0  1

統計該位置前放置元素的個數,為0

接著放第二大元素8,將第四個位置置1

0  0  0  1  0  0  1

統計該位置前放置元素的個數,為0

繼續放第三大元素6,將第六個位置置1

0  0  0  1  0  1  1

統計該位置前放置元素的個數,為1…

… 這樣直到把最小元素放完,累加每次放元素是該元素前邊已放元素的個數,這樣就算出總的逆序數來了

在統計和計算每次放某個元素時,該元素前邊已放元素的個數時如果乙個乙個地數,那麼一趟複雜度為o(n),總共操作n趟,複雜度為o(n^2),和第一種方法的複雜度一樣了,那我們為什麼還用這麼複雜的方法

當然,在每次統計的過程中用樹狀陣列可以把每一趟計數個數的複雜度降為o(logn),這樣整個複雜度就變為o(nlogn)

樹狀陣列是一種很好的資料結構,這有一篇專門描述樹狀陣列的文章

將序列中的每個數按照從大到小的順序插入到樹狀陣列中,給當前插入節點及其父節點的個數加1,然後統計該節點下邊及右邊放置元素的個數

具體**如下:

[cpp]view plain

copy

//用樹狀陣列求逆序數

const

intlength=8;  

struct

node  

;  int

lowbit(

intnum)  

//自定義排序方法

intcmp(

const

void

* num1,

const

void

* num2)  

//統計pos節點下方及右方節點元素的個數

intsum(

int*treearray,

intpos)  

return

result;  

}  //使pos位置的節點及其父節點的元素個數加1

void

inc(

int*treearray,

intpos)  

}  void

insertnum(

int*treearray,node * seq,

int&reversenum)  

}  intmain(

intargc, 

char

* argv)  

;  node seq[length];  

inttreearray[length];  

memset(treearray,0,sizeof

(treearray));  

for(

inti=1;i

//從大到小排序

qsort(seq+1,length-1,sizeof

(node),cmp);  

intreversenum=0;  

//邊插入邊計數

insertnum(treearray,seq,reversenum);  

cout0;  

}  

逆序數的幾種求法

求乙個數列的逆序數 逆序對 數列a 1 a 2 a 3 中的任意兩個數a i a j i,如果a i a j 那麼我們就說這兩個數構成了乙個逆序對 逆序數 乙個數列中逆序對的總數 如數列3 5 4 8 2 6 9 5,4 是乙個逆序對,同樣還有 3,2 5,2 4,2 等等 那麼如何求得乙個數列的逆...

逆序數的幾種求法

求乙個數列的逆序數 逆序對 數列a 1 a 2 a 3 中的任意兩個數a i a j i,如果a i a j 那麼我們就說這兩個數構成了乙個逆序對 逆序數 乙個數列中逆序對的總數 如數列3 5 4 8 2 6 9 5,4 是乙個逆序對,同樣還有 3,2 5,2 4,2 等等 那麼如何求得乙個數列的逆...

逆序數的幾種求法

什麼叫逆序數 對於某乙個數來說,它的逆序數等於在它之前有多少個比它大的數 對於某乙個序列來說,逆序數等於所有數的逆序數之和 例如 序列 5 1 5 2 逆序數 0 1 0 2 序列的逆序數 1 2 3 來看逆序數的求法 首先將定義乙個結構體,存數列的值和下標,然後按數值從大到小 數值相同按下標從大到...