一,問題描述
給定乙個陣列,求解該陣列中有多少組逆序對。比如 [7,5,6,4]一共有五對逆序對。分別是:(7,6),(7,5),(7,4),(6,4),(5,4)
二,演算法分析
有兩種方法來求解逆序對 的數目。一種是,對陣列中的每個元素,都與它後面的元素進行比較,若後面的元素比它小,則找到乙個逆序對。
這樣,第乙個元素與後面的n-1個元素比較,一共比較 n-1 次,第二個元素比較 n-2次,.....故一共需要比較 n(n+1)/2 次。時間複雜度為o(n^2)
另一種方式則是借助排序。試想,某些排序演算法(歸併、快速排序)只需要o(nlogn)就把整個陣列排好序了,那能不能利用排序來求逆序對 的數目?
其實,排序過程,就是乙個不斷地消除逆序對 的過程。對於有序陣列,它的逆序對為0。
而對於消除逆序對,在歸併排序中,合併兩個有序子陣列的過程 就是乙個消除逆序對的過程。
關於歸併排序可參考:
因此,只需要對歸併排序中的合併過程稍做修改,就可以求解逆序對的數目了。
三,**實現及分析
1其實,上面整個過程 與歸併排序的實現非常的相似。最重要的是第32行,計算逆序對數目。(與歸併排序比較,最主要的變化的也是第32行)public
class inversepairs 9//
類似於歸併排序中的 遞迴分解陣列
10private
static
int inversepairs(int arr, int tmparr, int left, int right)
20return 0;//
left==right 意味著子陣列中只有乙個元素.逆序對數目當然為0
21 }
22//
類似於 歸併排序中的 合併兩個有序子陣列
23private
static
int merge(int arr, int tmparr, int leftpos,
24int rightpos, int rightend) else
37 }
3839
while (leftpos <= leftend)
40 tmparr[tmppos++] = arr[leftpos++];
41while (rightpos <= rightend)
42 tmparr[tmppos++] = arr[rightpos++];
4344
for (int i = 0; i < numelements; i++, rightend--)
45 arr[rightend] = tmparr[rightend];
46return inversecount;
47 }
48//
for test purpose
49public
static
void main(string args) ;
51int result = inversepairs(arr);
52 system.out.println(result);
53 }
54 }
當 merge 兩個 陣列時:請注意,這兩個陣列是有序的。因此,逆序對的數目計算公式為:leftend - leftpos + 1
比如,如下示例:對於 5 而言,比4大。這說明 5 後面的元素都比 4 大。因此,對於 4 而言有 (5,4) (7,4)兩個逆序對。
另外,第15行-17行,也是對遞迴的理解(歸併排序是乙個遞迴過程,計算逆序對數目 也是乙個遞迴過程)
int leftpairs = inversepairs(arr, tmparr, left, center);//左半部分陣列的逆序對數目
int rightpairs = inversepairs(arr, tmparr, center + 1, right);//右半部分陣列的逆序對數目
currentpairs = merge(arr, tmparr, left, center + 1, right);//合併左右部分陣列時,消除的逆序對數目
return leftpairs + rightpairs + currentpairs;//最終返回 總的逆序對 的數目
四,參考資料
歸併排序應用之陣列中的逆序對數
在陣列中的兩個數字,如果前面乙個數字大於後面的數字,則這兩個數字組成乙個逆序對。輸入乙個陣列,求出這個陣列中的逆序對的總數。輸入 7,5,6,4 輸出 5 class solution def init self self.count 0def reversepairs self,nums list...
歸併排序求陣列中的逆序對
描述 測試說明與提交 提交狀態 題目設定 給定一組數,其中的兩個數字,如果前面乙個數字大於後面的數字,則這兩個數字組成乙個逆序對。輸入乙個陣列,求出這個陣列中的逆序對的總數。首先輸入資料組數t 1 t 100 每組測試資料報括兩行 第一行包含乙個整數n,表示陣列中的元素個數。其中1 n 10 5。第...
歸併排序 逆序數
對於數列a,將其二分地拆分為b,c 先將b,c分別排序好,再合併b,c即為總的排序,不過在合併的過程中我們可以算出逆序數哦。其原理網上很多,我這裡不再贅述,只給出實現 include include define ll long long using namespace std ll mergeso...