劍指offer-36:數組中的逆序對

參考:一、 https://www.geeksforgeeks.org/merge-sort/面試

           二、《劍指Offer:名企面試官精講典型編程題》編程

題目描述

在數組中的兩個數字,若是前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007數組

輸入描述:

題目保證輸入的數組中沒有的相同的數字ide

數據範圍:spa

  • 對於%50的數據,size<=10^4
  • 對於%75的數據,size<=10^5
  • 對於%100的數據,size<=2*10^5
示例1
輸入:1,2,3,4,5,6,7,0
輸出:7

 

解題思路1 (暴力法,時間複雜度: O(n^2))

這個方法最簡單直接,順序掃描整個數組,每掃到一個數,逐個比較其後面的全部數,若是構成逆序對,則計數+1。代碼沒啥難度,略了。指針

 

解題思路2 (分治法,時間複雜度: O(nlogn),空間複雜度:O(n))

 1     public int InversePairs(int[] array) {
 2         if (array.length < 2) {
 3             return 0;
 4         }
 5         return divideAndConquer(array, 0, array.length);
 6     }
 7 
 8     public int divideAndConquer(int[] array, int l, int r) {
 9         if (l >= r - 1) return 0;
10 
11         int mid = (l + r) / 2;
12         int count = 0;
13         int countL = divideAndConquer(array, l, mid);
14         int countR = divideAndConquer(array, mid, r);
15         int[] left = Arrays.copyOfRange(array, l, mid);
16         int[] right = Arrays.copyOfRange(array, mid, r);
17         int i = left.length - 1;
18         int j = right.length - 1;
19         for (int k = r - 1; k >= l; k--) {
20             // two cases: left is empty or right is empty
21             if (i < 0) {
22                 array[k] = right[j];
23                 j--;
24                 continue;
25             } else if (j < 0) {
26                 array[k] = left[i];
27                 i--;
28                 continue;
29             }
30             if (left[i] > right[j]) {
31                 count += j + 1;
32                 if (count > 1000000007) {
33                     count %= 1000000007;
34                 }
35                 array[k] = left[i];
36                 i--;
37             } else {
38                 array[k] = right[j];
39                 j--;
40             }
41         }
42         return (count + countL + countR) % 1000000007;
43     }

這一題的解法是在歸併排序(Merge Sort) 的基礎上修改獲得的。Merge Sort使用的是分治法的思想。分治法 (divide and conquer) 是將一個複雜的問題,分紅兩個或多個相同或者類似的子問題,再把子問題分紅更小的子問題,一直分到子問題足夠簡單來求解,最後再把子問題的解合併成原問題的解。歸併排序法的時間複雜度爲 O(nlogn),空間複雜度爲 臨時的數組 + 遞歸時壓入棧的數據佔用的空間 = n + logn,即 O(n)。code

 

以數組 {7, 5, 6, 4} 爲例進行分析,解題過程如圖5.2所示。首先將數組拆成至長度爲1的子數組,而後一邊合併一邊統計逆序對的數目。在第一對長度爲1 的子數組 {7}、{5}中,有一對逆序對 (7, 5)。在第二對長度爲1的子數組 {6}、{4}中,也有一對逆序對(6, 4)。在統計完這兩對逆序對後,須要分別對他們進行排序,如圖 5.2(c),以避免在之後的統計過程當中再重複統計。blog

 

而後須要統計兩個長度爲2的子數組之間的逆序對,併合並兩個子數組,如圖5.3所示。兩個子數組分別從末尾開始判斷數的大小,如圖5.2(a)中指針P一、P2。若是 P1 大於 P2,則構成逆序對,而且逆序對的數目等於第二個子數組中剩餘數字的個數,即 P2 以及以前的全部數字的個數。若是 P1 小於或等於 P2, 則不構成逆序對。每次比較的時候,咱們都把較大的數字從後往前複製到一個輔助數組,確保輔助數組中的數字是遞增排序的。在把較大的數字複製到輔助數組以後,把對應的指針向前移動一位,接下來進行下一輪比較。排序

以此類推,使用這個方法去求出其餘數組的逆序對個數。在解這一題的時候,還有一點須要注意,除了須要將總輸出結果 (count + countL + countR) 對1000000007取模,還要在計算過程當中將 count += j + 1 對1000000007取模,以避免count超出 Integer.MAX_VALUE。遞歸

相關文章
相關標籤/搜索