劍指Offer_#51_陣列中的逆序對
阿新 • • 發佈:2020-07-19
劍指Offer_#51_陣列中的逆序對
劍指offerContents
題目
在陣列中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個陣列中的逆序對的總數。
示例 1:
輸入: [7,5,6,4]
輸出: 5
限制:
0 <= 陣列長度 <= 50000
思路分析
最直接的思路是暴力迴圈,即用兩層迴圈迴圈判斷每一對數字是否是逆序對,然後計數,時間複雜度為,會超時。
所以必須時間複雜度更低的演算法,這裡用到了歸併排序中的merge
方法(詳見演算法第四版2.2節),只需要在標準的merge
方法中新增一行計數的程式碼,即可實現本題的需求。
解答
class Solution {
public int reversePairs(int[] nums) {
int len = nums.length;
if(len < 2) return 0;
int[] copy = new int[len];
//如果面試題不允許修改原陣列nums,就需要拷貝原陣列
for(int i = 0;i <= len - 1;i++){
copy[i] = nums[i];
}
int[] temp = new int[len];
return reversePairs(copy, 0, len - 1, temp);
}
//遞迴函式
private int reversePairs(int[] nums,int left,int right,int temp[]){
//出口條件:給出的[left...right]區間內只有一個元素,逆序對是0
if(left == right) return 0;
//遞推過程:分別計算當前區間左半段的逆序對,右半段的逆序對,跨越左右半段的逆序對,然後相加
//防止出現大數溢位
int mid = left + (right - left) / 2;
//計算[left...mid]區間內的逆序對
int leftPairs = reversePairs(nums, left, mid, temp);
//計算[mid+1...right]區間內的逆序對
int rightPairs = reversePairs(nums, mid + 1, right, temp);
//如果整個陣列都是有序的,那麼不需要繼續進行歸併了
if(nums[mid] <= nums[mid+1]) return leftPairs + rightPairs;
//歸併兩個有序的區間,然後計算這跨越兩個區間的逆序對數目
int crossPairs = mergeAndCount(nums, left, mid, right, temp);
return leftPairs + rightPairs + crossPairs;
}
private int mergeAndCount(int[] nums, int left, int mid,int right,int[] temp){
//複製nums到temp
for(int i = left;i <= right;i++){
temp[i] = nums[i];
}
int i = left;
int j = mid + 1;
int count = 0;
//歸併[left...mid]和[mid+1,right]兩個有序陣列,使得[left...right]範圍內的數字為有序
for(int k = left;k <= right;k++){
//i指標越界,直接歸併j指向的元素
if(i == mid + 1){
nums[k] = temp[j];
j++;
//j指標越界,直接歸併i指向的元素
}else if(j == right + 1){
nums[k] = temp[i];
i++;
//這裡不能寫<,否則連相等的數字對也會計算在內
}else if(temp[i] <= temp[j]){
nums[k] = temp[i];
i++;
}else{
nums[k] = temp[j];
j++;
//相比歸併排序中的merge()方法,僅僅增加了這一行程式碼
count += (mid - i + 1);
}
}
return count;
}
}