1. 问题描述
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:
Given nums = [5, 2, 6, 1]
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.
Return the array [2, 1, 1, 0].
2. 问题分析
查询参考“逆序数”
3. 初步解法
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
vector<int> result;
vector<int> copy(nums);
sort(nums.begin(),nums.end(), compare);
for(int i = 0; i < copy.size(); i++) {
for(int j = 0; j < nums.size(); j++) {
if(copy[i] == nums[j]) {
int count = (i > j)? i:j;
result.push_back(nums.size()-count-1);
}
}
}
return result;
}
static bool compare(int a, int b) {
return a > b;
}
};
但是这一解法把所有的elements当成互异的,所以忘记考虑当有元素相同的时候,这一算法不能AC(WR),并且时间复杂度也并不优于下面的解法,其最糟糕的情况也是 O(n2) 。(sort排序的复杂度为 O(nlogn) )。虽然下面的solution可以AC,但是也是简单粗暴地遍历 ,时间复杂度为 O(n2) 。
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
vector<int> result;
for(int i = 0; i < nums.size(); i++) {
int ans = 0;
for(int j = i+1; j < nums.size(); j++) {
if(nums[i] > nums[j] ) {
ans++;
}
}
result.push_back(ans);
}
return result;
}
};
4. 算法优化
采用分治的思想,结合归并排序和逆序数的特点,将原数组等分为两个sub arrays,针对左边的子数组的每一项A,遍历右边子数组,可以计算出A相对于右边子数组的逆序数。以此类推,当递归进行到剩余两项的时候,通过回溯,左边子数组(只有一个元素)便可以求出所有的逆序数。算法的小窍门是引入pair,将nums中的元素与其逆序数个数绑定,便于计算。算法的时间复杂度为 O(nlogn)
class Solution {
public:
void mergeSort(vector<pair<int, int> >&nums, int low, int high, vector<int>&ans) {
//最小单元大小为2时,返回计算逆序数,然后开始归并
if(low + 1 == high)
return;
//进行划分
int mid = (low + high)/2;
int right = mid;
mergeSort(nums, low, mid, ans);
mergeSort(nums, mid, high, ans);
//开始计算逆序数
for(int i = low; i < mid; i++) {
while(right < high && nums[i].first > nums[right].first)
right++;
ans[nums[i].second] += (right - mid);
}
//开始归并,调用STL的inplace_merge
inplace_merge(nums.begin() + low, nums.begin() + mid, nums.begin() + high);
}
vector<int> countSmaller(vector<int>& nums) {
if(nums.size() == 0)
return {};
int size = nums.size();
vector<int> ans(size, 0);
vector<pair<int, int> > vectorToPair;
for(int i = 0; i < size; i++)
vectorToPair.push_back(make_pair(nums[i], i));
mergeSort(vectorToPair, 0, size, ans);
return ans;
}
};
继续优化
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct Node {
Node* left;
Node* right;
int value;
int dupl;//与当前节点同值的元素个数
int leftCount;//当前节点的左子树的节点个数
Node(int v):left(NULL),right(NULL),value(v),dupl(1),leftCount(0){}
};
class BST {
private:
Node * root;
public:
BST(int v) {
root = new Node(v);
}
void insert(int value, int index, vector<int>&nums) {
Node* node = root;
int SmallerCount = 0;//逆序数个数
while(true) {
if(node->value == value) {
node->dupl++;
//注意:在BST上遍历过的节点加上同值节点上的左节点个数
nums[index] = SmallerCount + node->leftCount;
break;
} else if(node->value > value) {
node->leftCount++;
if(node->left != NULL) {
node = node->left;
} else {
nums[index] = SmallerCount;
node->left = new Node(value);
break;
}
} else {
SmallerCount += node->leftCount + node->dupl;
if(node->right != NULL) {
node = node->right;
} else {
nums[index] = SmallerCount;
node->right = new Node(value);
break;
}
}
}
}
};
vector<int> countSmaller(vector<int>& nums) {
int size = nums.size();
if(size == 0)
return {};
//倒序插入
BST bst(nums[size - 1]);
vector<int> result(size, 0);
for(int i = size - 2; i >= 0; i--)
bst.insert(nums[i], i, result);
return result;
}
int main() {
vector<int> nums;
nums.push_back(5);
nums.push_back(2);
nums.push_back(6);
nums.push_back(1);
vector<int> ans = countSmaller(nums);
for(int i = 0; i < ans.size(); i++) {
cout<<ans[i] << " ";
}
}