在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數。
示例 1:
輸入: [7,5,6,4]
輸出: 5
限制:
0 <= 數組長度 <= 50000
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
思路分析
最開始思路是動態規劃,但是理所應當的超時了,代碼如下:
class Solution {
public:
int reversePairs(vector<int>& nums) {
if(nums.size() <= 0)
return 0;
vector<int> dp(nums.size() + 1,0);//dp[i]表示從i開始到末尾的最大逆序對數目
dp[nums.size() - 1] = 0;
for(int i = nums.size() - 1;i >= 0;--i){
for(int j = i + 1;j < nums.size();++j){
if(nums[i] > nums[j]){
dp[i] = max(dp[i],dp[j]) + 1;
}
else
dp[i] = max(dp[i],dp[j]);
}
}
return dp[0];
}
};
第二種思路,歸併算法,很遺憾,還是超時。。。
雖然超時,但是這裏記錄一下爲什麼會想到歸併呢:
看下流程:
第一步,將左邊的10填入數組:
第二步,將右邊的12填入數組,並且此時在左邊區域的所有數值都比12大,那麼此時逆序對數目加(mid - i + 1)
第三步,將左邊14放入數組
第四步,將21放入數組,同理,計算左邊區域個數
後續進行同樣的操作,最後結果是:
這就是大致思路
class Solution {
int merge(vector<int>& nums, vector<int> temp, int left, int right){
if(left >= right)
return 0;
int mid = left + (right - left) / 2;
int count = merge(nums,temp,left,mid) + merge(nums,temp,mid + 1,right);//右區間
for(int i = left;i <= right;++i)
temp[i] = nums[i];
int i = left,j = mid + 1;
for(int k = left;k <= right;++k){
if(i == mid + 1){
nums[k] = temp[j];
++j;
}
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;
count += (mid - i + 1);
}
}
return count;
}
public:
int reversePairs(vector<int>& nums) {
vector<int> temp(nums.size());
return merge(nums, temp, 0,nums.size() - 1);
}
};
值得幸運的是,在討論區看見同校北郵師兄的優化方案,藉以解決,他的代碼優化如下:
計組upper_bound()函數,簡化了歸併的那一步操作。
後附上師兄leetcode主頁鏈接:https://leetcode-cn.com/u/mike-meng/,全站排名19的大佬哦!
class Solution {
public:
int dfs(vector<int> & nums,int l,int r){
if(r-l <= 1) return 0;
int mid = (l+r)>>1;
int res = dfs(nums,l,mid) + dfs(nums,mid,r);
sort(nums.begin()+l,nums.begin()+mid);
for(int i = mid; i < r; ++i)
res += nums.begin() + mid - upper_bound(nums.begin()+l,nums.begin()+mid,nums[i]);
return res;
}
int reversePairs(vector<int>& nums) {
return dfs(nums,0,nums.size());
}
};