315. Count of Smaller Numbers After Self
题目大意
给你一个数组,要求求出每个数后面比其小的数的个数。
例如[5,6,3,1],应该出[2,2,1,0]的答案。
解题方法
思路一
直接使用查找每个数后面比起小的个数,使用两个for循环搞定,时间复杂度是o(n^2)。
思路二
从后往前构建一颗二叉搜索树,并维护记录一个右子树节点个数的值,在每次插入节点的时候就可以更新,并且在每次插入的时候,可以根据维护的值去计算比这个数小的个数,即我们需要得到的答案。该该方法可以在一定程度上减小时间复杂度,但是在最坏情况下,仍为O(n^2)。
思路三
我们再回顾一下最暴力的方法,一次遍历每一个数,对这个数的右边的每一个数进行枚举,统计小于该数的个数。前一个过程是必须的,必须去访问每一个数,然后得到其后面比起小的个数。在后一个统计的过程中,我们是否可以使用其它有效的方法来降低其时间复杂度。
和思路二很像,为了快速的去统计一个数后面比起小的数,我们需要从右边开始考虑这个数组,也就是在处理过的基础上,一定包含了一定的信息,我们需要利用起来。
在考虑第i个元素的时候,我们其实已经对i+1到最后的元素进行了处理,所以需要使用一个方式来记录下前面的状态,并在加入第i个元素后,很快的找到我们需要的信息(比其小的个数)。
也就是说,现在我们把问题转换成如下形式:
依次向一个方法中加入数据,加入后可以在比O(N)小的时间内获取比加入数小的个数,其中加入该数的时间复杂度也应该小于O(N)
其实,我们就可以使用线段树了,那么什么是线段树呢?
简单说来,线段树就是将一个线段化的树,根节点是一个区间,左右子节点为该区间的划分,这个划分是按照等半执行的,所以一个[0,n]的区间,最终会有logN层。在上面我们可以快速的实现区间的更新和查询,其时间复杂度都是O(logn)的。
关于线段树的详细资料可以查看线段树一以及线段树二。
在拥有线段树的姿势后,我们就可以大概给出解题的方法了:
1. 建立线段树[0,max(array[i])],并对每个节点赋值为0
2. 从右向左依次加入数组中的元素,并对线段树做一下更新:区间[array[i]+1,max(array[i])]加一
3. 查询[array[i],array[i]]节点的值
以上方法在array[i]很大的时候,将没办法存储下线段树(时间复杂度上,我感觉压力应该不是很大,毕竟有个log),所以需要进行离散化。
1. 对数组进行去重排序,得到数组dul,假设其大小为sizedul
2. 建立线段树[0,sizedul-1]
3. 从右向左依次遍历原数组中的元素,并在dul中获得该元素的小标k,我们对线段树做一下更新,区间[k,sizedul-1]加1
4. 查询线段树[k,k]即为该元素的ans,继续3知道数组完成遍历。
其实第一步也就是传说中的离散化,我好行不是很明白的样子,就按照别人的说法给写了出来,下面是c代码。
/**
* Return an array of size *returnSize.
* Note: The returned array must be malloced, assume caller calls free().
*/
int min(int a, int b){
return a>b?b:a;
}
//merge sort
void mergeSort(int *a, int *tmp, int l, int r){
if(l == r) return;
int mid = (l+r)/2;
mergeSort(a, tmp, l, mid);
mergeSort(a, tmp, mid+1, r);
//merge
int first = l, second = mid+1;
int i = l;
while(first <= mid && second <= r){
if(a[first] > a[second]){
tmp[i++] = a[second++];
}
else{
tmp[i++] = a[first++];
}
}
while(first <= mid){
tmp[i++] = a[first++];
}
while(second <= r){
tmp[i++] = a[second++];
}
for( i=l; i<=r; i++){
a[i] = tmp[i];
}
}
//remove the duplicate from the sorted array
void removeDul(int *a, int n, int *retSize){
int now = 1;
*retSize = 1;
while(now < n){
if(a[now] == a[(*retSize) - 1]) now++;
else{
a[(*retSize) ++] = a[now++];
}
}
}
//get the index of the num from sorted array
int getIndex(int *a, int n, int k){
int l = 0, r = n-1;
int mid;
while(l <= r){
mid = (l+r)/2;
if(a[mid] == k) return mid;
if(a[mid] > k) r = mid-1;
else l = mid+1;
}
//useless but for the compiling
return mid;
}
struct node{
int l,r;
int cnt;
int add;
int flag;
};
//build the bst; node from zore
void build(struct node *tree, int num, int l, int r){
// printf("%d %d %d \n", num, l, r);
tree[num].l = l;
tree[num].r = r;
tree[num].cnt = tree[num].add = tree[num].flag = 0;
if(l == r) return;
int mid = (l+r)/2;
build(tree, 2*num+1, l, mid);
build(tree, 2*num+2, mid+1, r);
}
//update the bst
void update(struct node *tree, int num, int l, int r, int k){
int ll = tree[num].l;
int rr = tree[num].r;
int flag = tree[num].flag;
int add = tree[num].add;
if(ll > r || rr < l) return;
if(ll >= l && rr <= r){
tree[num].add += k;
tree[num].flag = 1;
tree[num].cnt += k;
return;
}
//put down the flag to child
if(flag){
tree[2*num+1].add += add;
tree[2*num+1].flag = 1;
tree[2*num+1].cnt += add;
tree[2*num+2].add += add;
tree[2*num+2].flag = 1;
tree[2*num+2].cnt += add;
//del the self flag;
tree[num].add = 0;
tree[num].flag = 0;
}
//update the child
update(tree, 2*num+1, l, r, k);
update(tree, 2*num+2, l, r, k);
}
//query
int query(struct node *tree, int num, int k){
int ll = tree[num].l;
int rr = tree[num].r;
int flag = tree[num].flag;
int add = tree[num].add;
if(ll > k || rr < k) return 0;
if(rr == k && ll == k) {
return tree[num].cnt;
}
//put down the flag to child
if(flag){
tree[2*num+1].add += add;
tree[2*num+1].flag = 1;
tree[2*num+1].cnt += add;
tree[2*num+2].add += add;
tree[2*num+2].flag = 1;
tree[2*num+2].cnt += add;
//del the self flag;
tree[num].add = 0;
tree[num].flag = 0;
}
int ans = 0;
ans += query(tree, 2*num+1, k);
ans += query(tree, 2*num+2, k);
return ans;
}
int* countSmaller(int* nums, int numsSize, int* returnSize) {
if(numsSize == 0) return NULL;
int dul[numsSize], org[numsSize];
//copy the nums;
for(int i=0; i<numsSize; i++){
org[i] = nums[i];
}
printf("\n");
mergeSort(nums, dul, 0, numsSize-1);
int dulSize;
removeDul(dul, numsSize, &dulSize);
struct node tree[4*numsSize];
//build the bst
build(tree, 0, 0, dulSize-1);
int *ret = malloc(sizeof(int)*numsSize);
for(int i=numsSize-1; i>=0; i--){
int de = getIndex(dul, dulSize, org[i]);
update(tree, 0, de+1, dulSize-1, 1);
int ans = query(tree, 0, de);
ret[i] = ans;
}
*returnSize = numsSize;
return ret;
}