陣列中的逆序對(分治、遞迴與合併)
阿新 • • 發佈:2018-12-14
在陣列中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。
輸入一個數組,求出這個陣列中的逆序對的總數P。
將P對1000000007取模的結果輸出。 即輸出P%1000000007
輸入描述:
題目保證輸入的陣列中沒有的相同的數字
資料範圍:
對於%50的資料,size<=10^4
對於%75的資料,size<=10^5
對於%100的資料,size<=2*10^5
示例:
輸入
1,2,3,4,5,6,7,0
輸出
7
思路:暴力解法是雙層迴圈逐一判斷,時間複雜度為O(n^2). 本題的更優解法是分治與合併。該題的思路過程可以參考劍指OFFER上的解釋,不再贅述,下面只寫出我的思路。首先,
對陣列的排序過程用分治法,其中劃分的部分和統計逆序對數是一致的,所以共同完成;而合併的部分就是將兩個有序的子數組合併成大的有序陣列,這需要額外的儲存空間。比如陣列nums[0,9] 劃分成a[0,4]和b[5,9],合併a和b時需要有一個長度為10的輔助空間copy[0,9],存放排好序的元素。當遞迴到上一層時,不需要把copy的值複製給nums,而是調換兩者的位置(這一點非常重要)。上一層的copy就是下一層的nums, 上一層的nums就是下一層的copy。 從而我們需要合併的陣列總是當前排序程度更優的那一個,而當前排序程度更差的那個陣列作為輔助空間,來儲存當前合併排序過後的元素。
程式碼:
//函式功能:把data陣列的[start,end]區間排序後儲存在copy陣列的[start,end]區間內;
// 同時統計該區間內的逆序對數。
// 在區間[start,end]內data和copy的元素相同(雖然順序不同)。
long long InversePairsCore(vector<int> &data, vector<int> ©, int start, int end){
if (start == end){
copy[start] = data[start];
return 0;
}
int length = (end - start) / 2;
//區間[start,end]一分為二:[start,mid]和[mid+1,end];
//把copy陣列的左部分排序並存入data左部分,統計左部分的逆序數left
long long left = InversePairsCore(copy, data, start, start + length);
//把copy陣列的右部分排序並存入data右部分,統計右部分的逆序數right
long long right = InversePairsCore(copy, data, start + length + 1, end);
//把兩部分有序的data合併然後存入copy,統計左部分與右部分之間的逆序數count
int i = start + length;
int j = end;
int indexcopy = end;
long long count = 0;
while (i >= start&&j >= start + length + 1){
if (data[i]>data[j]){
copy[indexcopy--] = data[i--];
count+=j - start - length;
}
else
copy[indexcopy--] = data[j--];
}
//左右部分中有一個已經取完,則另一個依次加入copy即可
for (; i >= start; i--)
copy[indexcopy--] = data[i];
for (; j >= start + length + 1; j--)
copy[indexcopy--] = data[j];
return left + right + count;
}