在數組中的兩個數字,如果前面一個數字大於后面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數:
如數組{7,5,6,4},逆序對總共有5對,{7,5},{7,6},{7,4},{5,4},{6,4};
思路1:暴力解法,順序掃描整個數組,每掃描到一個數字的時候,逐個比較該數字和它后面的數字的大小。如果后面的數字比它小,則這兩個數字就組成一個逆序對。假設數組中含有n個數字,由於每個數字都要和O(n)個數字作比較,因此這個算法的時間復雜度是O(n2)。
思路2:分治思想,采用歸並排序的思路來處理,如下圖,先分后治:
先把數組分解成兩個長度為2的子數組,再把這兩個子數組分解成兩個長度為1的子數組。接下來一邊合並相鄰的子數組,一邊統計逆序對的數目。在第一對長度為1的子數組{7}、{5}中7>5,因此(7,5)組成一個逆序對。同樣在第二對長度為1的子數組{6},{4}中也有逆序對(6,4),由於已經統計了這兩對子數組內部的逆序對,因此需要把這兩對子數組進行排序,避免在之后的統計過程中重復統計。
逆序對的總數=左邊數組中的逆序對的數量+右邊數組中逆序對的數量+左右結合成新的順序數組時中出現的逆序對的數量;
總結統計數組逆序對的過程:先把數組分隔成子數組,先統計出子數組內部的逆序對的數目,然后再統計出兩個相鄰子數組之間的逆序對的數目。在統計逆序對的過程中,還需要對數組進行排序,其實這個排序過程就是歸並排序的思路。
代碼實現思路如下:
//數組中的逆序對 public static int InversePairs(int[] array){ if(array==null||array.length<=1) return 0; int[] copy = new int[array.length]; for(int i=0;i<array.length;i++){ copy[i] = array[i]; } return mergeCount(array, copy, 0, array.length-1); } public static int mergeCount(int[] array, int[] copy, int start, int end){ if(start==end){ copy[start] = array[start]; return 0; } int mid = (start+end)>>1; int leftCount = mergeCount(copy, array, start, mid); int rightCount = mergeCount(copy, array, mid+1, end); int i = mid;//i初始化為前半段最后一個數字的下標 int j = end;//j初始化為后半段最后一個數字的下標 int index = end;//輔助數組復制的數組的最后一個數字的下標 int count = 0;//計數--逆序對的數目 while(i>=start&&j>=mid+1){ if(array[i]>array[j]){ copy[index--] = array[i--]; count += j-mid; }else{ copy[index--] = array[j--]; } } for(;i>=start;i--){ copy[index--] = array[i]; } for(;j>=mid+1;j--){ copy[index--] = array[j]; } return leftCount+rightCount+count; }