給定乙個陣列 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我們就將 (i, j) 稱作乙個重要翻轉對。
你需要返回給定陣列中的重要翻轉對的數量。
示例 1:
輸入: [1,3,2,3,1]
輸出: 2
示例 2:
輸入: [2,4,3,5,1]
輸出: 3
注意:
給定陣列的長度不會超過50000。
輸入陣列中的所有數字都在32位整數的表示範圍內。
思路分析:這道題雖然要求簡單,但是需要優化演算法,否則無法通過大量的測試資料。可能大家直接來暴搜,這種演算法肯定通不過。我們採取「歸併排序」策略。
首先我們需要了解,nums[ i ]的「翻轉對」 nums[ j ]只要它在j的後面就行,並不會影響nums[ i ]的「翻轉對」的計算。(比如在蠻力法中的兩層for迴圈)我們現在將採取「歸併排序」。
處理nums[left, right]
第一步:處理 [left, mid] (遞迴)
第二步:處理[mid + 1, right] (遞迴)
第三步:計算左段各個元素在右端構成的 「重要翻轉對」(由於排序左(右)段已經計算了左(右)段內產生的「重要翻轉對」,所以這裡只需要計算兩個段之間的「重要翻轉對」)
第四步:將兩段有序合併
class
solution
vector<
int>
mergevec
(nums.
size()
);sort(0
, nums.
size()
-1, nums, mergevec)
;//歸併排序
return result;
}//處理nums[left, right]
void
sort
(int left,
int right, vector<
int>
& data, vector<
int>
&mergevec)
int mid =
(left + right)/2
;sort
(left, mid, data, mergevec)
;//第一步:處理 [left, mid] (遞迴)
sort
(mid +
1, right, data, mergevec)
;//第二步:處理[mid + 1, right] (遞迴)
int i = left;
int j = mid +1;
//第三步:計算左段各個元素在右端構成的 「重要翻轉對」(由於排序左(右)段已經計算了左(右)段內產生的「重要翻轉對」,所以這裡只需要計算兩個段之間的「重要翻轉對」)
while
(j < right +
1&& i < mid +1)
else
}//第四步:有序合併左右兩段
merge
(left, right, data, mergevec)
;//data中[left, mid]和[mid + 1, right]兩段進行合併到result中[left, right]
//複製回data
for(
int i = left; i <= right; i++)}
//將data中[left, mid]和[mid + 1, right]兩段進行合併到result中[left, right]
void
merge
(int left,
int right, vector<
int>
& data, vector<
int>
&mergevec)
else
}//下面的迴圈只會執行乙個
LeetCode 493 翻轉對 歸併排序
如果 i j 分別屬於兩個有序區間,並且 nums i 2 nums j 則大於 i 的元素也都滿足需求,利用有序特點可以減少重複的比較操作。將兩個有序的陣列合併成乙個有序陣列稱為歸併。歸併排序包含了兩個過程 從上往下的分解 把當前區間一分為二,直至分解為若干個長度為1的子陣列 從下往上的合併 兩個...
leetcode493(翻轉對 歸併排序)
給定乙個陣列 nums 如果 i j 且 nums i 2 nums j 我們就將 i,j 稱作乙個重要翻轉對。你需要返回給定陣列中的重要翻轉對的數量。示例 1 輸入 1,3,2,3,1 輸出 2 示例 2 輸入 2,4,3,5,1 輸出 3 題解 一 歸併排序,在歸併排序的過程中,假設對於某歸併陣...
leetcode 歸併排序
首先對倆個有序陣列進行組合排序 def merge two vec nums1,nums2,res i 0 j 0 len i len nums1 len j len nums2 while i len i and j len j if nums1 i nums2 j i 1else j 1 res...