题目:https://leetcode.com/problems/reverse-pairs/description/
题目类别:分治法
题目难度:hard
题目描述:
Given an array nums
, we call (i, j)
an important reverse pair if i < j
and nums[i] > 2*nums[j]
.
You need to return the number of important reverse pairs in the given array.
Example1:
Input: [1,3,2,3,1]
Output: 2
Example2:
Input: [2,4,3,5,1]
Output: 3
Note:
- The length of the given array will not exceed
50,000
. - All the numbers in the input array are in the range of 32-bit integer.
题目分析:
对于给定的数组nums,满足i < j 且nums[i] > 2 * nums[j]的数对称为重要逆序对,需要求出重要逆序对的总对数。对此我们采用分治法去解决,定义T(i, j)为求出i到j之间的重要逆序对,我们有两种分治策略:
1.顺序递推(sequential recurrence):
T(i, j) = T(i, j - 1) + C
将求出i到j之间的重要逆序对划分为两个子问题:
- T(i, j - 1):求出i到j-1之间的重要逆序对
- C:求出num[i, j-1]中大于2 * nums[j]与num[j]所形成的重要逆序对
BST(binary search tree):
此方法虽然比暴力求解快很多,但是因为BST不是自平衡的,所以仍然无法保证O(nlogn)的时间复杂度,可考虑使用
红黑树或AVL树,下面为普通BST的实现。
class Node {
public:
int val;
int cnt; //the total number of elements in the subtree rooted at current node that are greater than or equal to 'val'
Node *left, *right;
Node(int val) {
this->val = val;
this->cnt = 1;
left = NULL;
right = NULL;
}
Node() {
left = NULL;
right = NULL;
}
};
class Solution {
private:
//insert node correspond to value to tree
Node* insert(Node * root, int val) {
if (root == NULL) {
root = new Node(val);
} else if (val == root->val) {
root->cnt++;
} else if (val < root->val) {
root->left = insert(root->left, val);
} else {
root->cnt++;
root->right = insert(root->right, val);
}
return root;
}
//search the count of value which is no smaller than 'val' in the tree 'root'
int search(Node * root, long val) {
if (root == NULL) {
return 0;
} else if (val == root->val) {
return root->cnt;
} else if (val < root->val) {
return root->cnt + search(root->left, val);
} else {
return search(root->right, val);
}
}
public:
//calculate the number of pairs while construct the tree
int reversePairs(vector<int>& nums) {
int count = 0; //
Node* root = new Node;
for (auto ele : nums) {
count = count + search(root, 2L * ele + 1); //'2L' to avoid overflow
root = insert(root, ele);
}
return count;
}
};
BIT(binary index tree):
此方法时间复杂度为O(nlogn),BIT介绍参看:http://www.cnblogs.com/whensean/p/6851018.html
class Solution {
public:
int search(vector<int>& bit, int i) {
int sum = 0;
while (i < bit.size()) {
sum += bit[i];
i += i & -i;
}
return sum;
}
void insert(vector<int>& bit, int i) {
while (i > 0) {
bit[i] += 1;
i -= i & -i;
}
}
int index(vector<int>& arr, long val) {
int l = 0, r = arr.size() - 1, m = 0;
while (l <= r) {
m = l + ((r - l) >> 1);
if (arr[m] >= val) {
r = m - 1;
} else {
l = m + 1;
}
}
return l + 1;
}
int reversePairs(vector<int>& nums) {
int count = 0;
vector<int> copy(nums);
vector<int> bit(copy.size() + 1);
std::sort(copy.begin(), copy.end());
for (auto ele : nums) {
count = count + search(bit, index(copy, 2L * ele + 1));
insert(bit, index(copy, ele));
}
return count;
}
};
2.划分递推( partition recurrence):
T(i, j) = T(i, m) + T(m + 1, j) + C(m = (i+j)/2)
将求出i到j之间的重要逆序对划分为两个子问题:
- T(i, m):求出i到m之间的重要逆序对
- T(m+1, j):求出i到m之间的重要逆序对
- C:求出num[i, m]中元素与num[m+1, j]中元素所形成的重要逆序对
class Solution {
public:
int reversePairs(vector<int>& nums) {
return reversePairsSub(nums, 0, nums.size() - 1);
}
int reversePairsSub(vector<int>& nums, int l, int r) {
if (l >= r) return 0;
int m = l + ((r - l) >> 1);
int count = reversePairsSub(nums, l, m) + reversePairsSub(nums, m + 1, r);
int i = l, j = m + 1, k = 0, p = m + 1;
vector<int> merge(r - l + 1);
while (i <= m) {
while (p <= r && nums[i] > 2L * nums[p]) p++;
count += p - (m + 1);
while (j <= r && nums[i] >= nums[j]) merge[k++] = nums[j++];
merge[k++] = nums[i++];
}
while (j <= r) merge[k++] = nums[j++];
for(int a = 0; a < merge.size(); a++) {
nums[l+a] = merge[a];
}
return count;
}
};
参考文献:
https://discuss.leetcode.com/topic/79227/general-principles-behind-problems-similar-to-reverse-pairs