leetcode [Divide and Conquer] No.315 Count of Smaller Numbers After Self
題目描述
You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].
Example:
Input: [5,2,6,1] Output: [2,1,1,0] Explanation: To the right of 5 there are 2 smaller elements (2 and 1). To the right of 2 there is only 1 smaller element (1). To the right of 6 there is 1 smaller element (1). To the right of 1 there is 0 smaller element.
解題思路
1. 思路一
看完題目第一個想法也是最樸素的想法便是重迴圈,對於每一個數,遍歷其後的所有數,統計小於其數字的個數,很容易看出,這種演算法的效率為。 程式碼:
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
vector<int> res;
for (int i = 0; i < nums.size(); i++) {
int count = 0;
for (int j = i + 1; j < nums.size(); j++) {
if (nums[i] > nums[j]) {
count++;
}
}
res.push_back(count);
}
return res;
}
};
執行結果: 可見,的演算法效率不是很高。
2. 思路二
經過一定的觀察與思考我們可以發現,統計這個某個數字後面比這個數字小的數字個數,不就是統計包含這個數字的逆序對個數嗎,而我們可以利用歸併排序的過程來求一個數組的逆序對個數,其演算法效率為
分治思路:將原陣列分為左右兩個陣列,則左邊陣列中某個元素的逆序對數 = 其在左陣列中的逆序對數 + 右陣列中比它小的元素個數。
具體來說,就是每次將陣列從中間平分成兩個陣列,分別對左右陣列進行排序,排序完畢之後,進行歸併操作(即首先用兩個指標分別指向左右兩個陣列的第一個元素,比較兩個元素的大小,將小的那個元素加入有序陣列,並將相應的指標後移一位)。在歸併操作過程中,每當將右邊陣列的元素加入有序佇列的時候,說明該元素比當前左邊陣列剩餘的所有元素都要小,則左邊所有元素的統計結果均加一。
程式碼:
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
//統計計算結果的向量
vector<int> res(nums.size(), 0);
//用於歸併排序的向量,其中pair的第一個元素表示值,第二個元素表示該元素在初始陣列中的座標
vector<pair<int, int> > vec;
for (int i = 0; i < nums.size(); i++) {
vec.push_back(make_pair(nums[i], i));
}
//使用歸併排序的方法,計算結果
subCounter(vec, res);
return res;
}
private:
void subCounter(vector<pair<int, int> >& nums, vector<int>& res) {
//遞迴終止條件
if (nums.size() <= 1) return;
//將陣列平分為左右兩個陣列
int mid = nums.size() / 2;
vector<pair<int, int> > left(nums.begin(), nums.begin() + mid);
vector<pair<int, int> > right(nums.begin() + mid, nums.end());
//分別對左右陣列進行排序和計算逆序對個數
subCounter(left, res);
subCounter(right, res);
//歸併操作
vector<pair<int, int> > conquer;
auto liter = left.begin(), riter = right.begin();
int count = 0;
while (liter != left.end() && riter != right.end()) {
//左陣列的元素小,將它加入有序佇列,並累加相應的逆序對數
if (liter->first <= riter->first) {
conquer.push_back(*liter);
res[liter->second] += count;
liter++;
//右陣列的元素小,將它加入有序佇列,逆序對數+1
} else {
conquer.push_back(*riter);
count++;
riter++;
}
}
//將左右陣列中剩餘的元素加入有序佇列
while(liter != left.end()) {
conquer.push_back(*liter);
res[liter->second] += count;
liter++;
}
while(riter != right.end()) {
conquer.push_back(*riter);
riter++;
}
//將排序後的結果賦值給原無序陣列
nums.assign(conquer.begin(), conquer.end());
}
};
執行結果: 通過分治的策略,我們將演算法效率提升到了,而這種演算法效率大大縮短了執行時間。