Hard-题目11:315. Count of Smaller Numbers After Self

来源:互联网 发布:手机养狗软件 编辑:程序博客网 时间:2024/05/17 19:19

题目原文:
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].
题目大意:
给出一个数组,计算每个元素右边有几个比它小的元素,组成一个新的数组返回。
题目分析:
这里需要用到一种高级数据结构叫做“树状数组”,又叫Binary Indexed Tree或Fenwick tree,其特点是查询和修改节点都是O(logn)的,用于快速求数组的前n项和。
构建方法:
设原数组是a[1…n],按如图方式构造数组c:(from 百度百科)
这里写图片描述
C[1]=a[1],c[2]=a[1]+a[2],….c[8]=a[1]+…+a[8],
树状数组有两个api,这两个api的时间复杂度都是O(logn)的,n为数组长度,具体实现略,有兴趣可以自行百度。

void add(int* tree,int k,int num) // 在a[k]上增加num,更新树状数组treeint get(int* tree,int k) // 求a[1]+…+a[k]的和

再回到这道题,先求最小值和最大值,记为min和max,然后平移区间使得min=1,(若min不为1,则整个数组都减去min和1的差值)然后基于数组num(其中最小值修正为1,对应最大值为max-min+1) 构建树状数组tree[1…max-min+1],其中对应的a数组的a[i]代表num数组中i出现的次数。
接下来逆向扫描num数组,每扫到一个元素num[i],调用get函数求树状数组中a[1]到a[num[i]-1]的和,这个和值就是num数组中i右边比num[i]小的个数!!(这里最难理解,稍后说明),再调用add函数把num[i]加到a中。
时间复杂度:扫描一遍数组O(n),每个元素调用了两个O(logk)复杂度的api,其中k为树状数组长度,总复杂度为O(nlog(max-min)).
上面说了这么多有点抽象,下面举个栗子~~(看明白的童鞋或者嫌我墨迹的童鞋略过吧)
输入数组:[5,6,-1,1] (答案为:[2,2,0,0])
首先修正数组使得最小值为1(否则会出现数组越界):
nums[]=[6,8,1,3],min=1,max=8
然后构建tree[1..8](因为tree的值很难写,就写图中下方的a数组)
逆向扫描数组:此时a数组初始化为全0
nums[i]=3,调用get函数求a[1]+a[2]为0(即1,2这两个数还没出现过),调用add函数使得a[3]++,
此时a数组:[0,0,1,0,0,0,0,0]
Nums[i]=1,调用get函数返回0(因为1左边没有数据了),调用add函数使得a[1]++.
此时a数组:[1,0,1,0,0,0,0,0]
Nums[i]=8,调用get函数求a[1]+…+a[7]=2,调用add函数使得a[8]++
此时a数组:[1,0,1,0,0,0,0,1]
Nums[i]=6,调用get函数求a[1]+…+a[5]=2,调用add函数使得a[6]++.
此时a数组:[1,0,1,0,0,1,0,1]
从下往上读取get的返回值,得到数组[2,2,0,0]即为答案。
源码:(language:java)

public class Solution {    public List<Integer> countSmaller(int[] nums) {        LinkedList<Integer> res = new LinkedList<Integer>();        if (nums == null || nums.length == 0) {            return res;        }        // find min value and minus min by each elements, plus 1 to avoid 0 element        int min = Integer.MAX_VALUE;        int max = Integer.MIN_VALUE;        for (int i = 0; i < nums.length; i++) {            min = (nums[i] < min) ? nums[i]:min;        }        int[] nums2 = new int[nums.length];        for (int i = 0; i < nums.length; i++) {            nums2[i] = nums[i] - min + 1;            max = Math.max(nums2[i],max);        }        int[] tree = new int[max+1];        for (int i = nums2.length-1; i >= 0; i--) {            res.addFirst(get(nums2[i]-1,tree));            update(nums2[i],tree);        }        return res;    }    private int get(int i, int[] tree) {        int num = 0;        while (i > 0) {            num +=tree[i];            i -= i&(-i);        }        return num;    }    private void update(int i, int[] tree) {        while (i < tree.length) {            tree[i] ++;            i += i & (-i);        }    }}

成绩:
8ms,beats 97.33%,众数11ms,8.79%
Cmershen的碎碎念:
返回类型是List,因为要从下往上读get函数的返回值,故使用链表的实现类LinkedList,因为提供了addFirst()方法.

0 0
原创粉丝点击