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.
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].
tags:Divide and Conquer / Binary Indexed Tree / Segment Tree / Binary Search Tree
1)最开始想到二分搜索(binary search)。思想是,维持一个vector<int> arr,初始为空。从nums的尾部向前遍历,对于每个元素,都在arr中进行binary search,返回该元素应该插入的位置i,i就是比该元素小的元素的个数。然后将该元素插入arr中。
开始以为这样的复杂度为O(lgn!)。但是我忘了vector的insert操作不是O(1),因为每次插入都要向后移动一部分元素,所以很费时间。虽然vector在binary search 中 的访问时间为O(1)。代码如下:
<span style="font-size:18px;">class Solution { //二分搜索O(lg(n!)) 1700ms左右
public:
vector<int> countSmaller(vector<int>& nums) {
vector<int> arr, res;
int size = nums.size(), pos;
for(int i = size-1; i >= 0; --i){
pos = biSearch(arr, nums[i]);
cout<<pos<<" ";
arr.insert(arr.begin()+pos, nums[i]);
res.insert(res.begin(), pos);
}
return res;
}
int biSearch(vector<int>& arr, int n){
int s = 0, e = arr.size()-1, mid;
float tmp = n - 0.5;
while(s<=e){
mid = (s+e)/2;
if(arr[mid]<tmp) s++;//因为是整数,不会出现相等的情况。
else e--;
}
return s;
}
};</span>
2)由1)中遇到的问题,可以想到用binary search tree,因为BST的搜索和插入时间都为lgN。这样时间复杂度就变成了N*O(lgN)。下面的代码是discuss中的,写的很好,40ms,值得学习。
<span style="font-size:18px;">class Solution {//BST
public:
struct Node {
int val, smaller; //smaller统计该节点左子树中节点的个数。
Node *left, *right;
Node(int value, int small) {
left=right=NULL, val=value, smaller=small;
}
};
int insert(Node *&root, int value) {//将一个元素插入到bst中,并返回树中比该元素小的元素的个数。
if (root==NULL)
return (root=new Node(value, 0)), 0;
if (value < root->val){
root->smaller++;
return insert(root->left, value);
}
else
return insert(root->right, value) + root->smaller + (value>root->val ? 1 : 0);
}
vector<int> countSmaller(vector<int>& nums) {
Node *root = NULL;
deque<int> ans; //deque可以从头开始插入元素,只需O(1),而vector需要O(n)
for (int i=nums.size()-1; i>=0; i--)
ans.push_front(insert(root, nums[i]));
return vector<int> (ans.begin(), ans.end());
}
};</span>
3)然后就是分治方法了。这个方法大体也想出来了。但是在merge时候,要维持一个index数组,用来保存某个数最原始的位置。代码如下,时间100ms,可能因为常数项大了点。
<span style="font-size:18px;">class Solution { //分治O(nlgn)
public:
vector<int> countSmaller(vector<int>& nums) {
int size = nums.size();
vector<int> res(size, 0);
vector<int> ind(size, 0);
for (int i = 0; i < size; ++i) ind[i] = i;
mergeSort(nums, 0, size - 1, res, ind);
return res;
}
void mergeSort(vector<int>& nums, int s, int e, vector<int>& res, vector<int>& ind){
if (s < e){
int mid = (s + e) / 2;
mergeSort(nums, s, mid, res, ind);
mergeSort(nums, mid + 1, e, res, ind);
merge(nums, s, mid, e, res, ind);
}
}
void merge(vector<int>& nums, int s, int mid, int e, vector<int>& res, vector<int>& ind){
vector<int> l, r, ind_l, ind_r;
l.push_back(INT_MAX);
r.push_back(INT_MAX);
for (int k = s; k <= mid; ++k){
l.push_back(nums[k]);
ind_l.push_back(ind[k]);
r.push_back(nums[k - s + mid + 1]);
ind_r.push_back(ind[k - s + mid + 1]);
}
int i = mid - s + 1, j = e - mid, cnt = 0;
for (int k = e; k >= s; --k){
if (l[i] > r[j]){
cnt++;
nums[k] = r[j];
ind[k] = ind_r[j - 1];
j--;
}
else{
nums[k] = l[i];
ind[k] = ind_l[i - 1];
res[ind_l[i - 1]] += cnt;
i--;
}
}
}
};</span>
4)至于binary indexed tree/segment tree。有空再补上。