315. Count of Smaller Numbers After Self

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].


就是求每个元素后面有多少个比它小的数,可以看成反的逆序数,借鉴求逆序数的思想。


方法:用树状数组(摘自http://blog.csdn.net/dlengong/article/details/7594919)

还是以刚才的序列

3  5  4  8  2  6  9

大体思路为:新建一个数组,将数组中每个元素置0

0  0  0  0  0  0  0

取数列中最大的元素,将该元素所在位置置1

0  0  0  0  0  0  1

统计该位置前放置元素的个数,为0

接着放第二大元素8,将第四个位置置1

0  0  0  1  0  0  1

统计该位置前放置元素的个数,为0

继续放第三大元素6,将第六个位置置1

0  0  0  1  0  1  1

统计该位置前放置元素的个数,为1

这样直到把最小元素放完,累加每次放元素是该元素前边已放元素的个数,这样就算出总的逆序数来了

在统计和计算每次放某个元素时,该元素前边已放元素的个数时如果一个一个地数,那么一趟复杂度为O(n),总共操作n趟,复杂度为O(n^2),和第一种方法的复杂度一样了,那我们为什么还用这么复杂的方法

当然,在每次统计的过程中用树状数组可以把每一趟计数个数的复杂度降为O(logn),这样整个复杂度就变为O(nlogn)


树状数组、线段树都可以。刚开始想试一下indexminpq/indexmaxpq,但是pq的思想和堆排序类似,不是稳定的。

应该想过另外的处理办法,先把数组排序,以值的大小为第一关键字,以index为第二关键字。

按照上面的方法,依次选出排序以后小的元素,相应的数组标记置为1,然后统计从当前开始右边的和(代表当前位置右边所有比元素小的和)

实际上是把树状数组倒一下来使用。

public class Solution {
    public List<Integer> countSmaller(int[] nums)
	{
		int len=nums.length;
		ArrayList<Integer> retlist=new ArrayList<>();
		storeunit[] arr=new storeunit[len];
		for(int i=0;i<len;i++)
			arr[i]=new storeunit(nums[i], i);
		
		int[] result=new int[len];
		int[] treearr=new int[len];
		
		Arrays.sort(arr);
		
		for(int i=0;i<len;i++)
		{
			int index=arr[i].index;
			add(len-index, 1, treearr);
			result[index]=read(len-index-1, treearr);
		}
		
		for(int i=0;i<len;i++)
			retlist.add(result[i]);
		
		return retlist;
	}
	
	public void add(int k,int num,int[] treearr)
	{
		while(k<treearr.length)
		{
			treearr[k]+=num;
			k+=k&-k;
		}
	}
	
	public int read(int k,int[] treearr)
	{
		int sum=0;
		while(k!=0)
		{
			sum+=treearr[k];
			k-=k&-k;
		}
		return sum;
	}
}

class storeunit implements Comparable<storeunit>
{
	int val;
	int index;
	public storeunit(int val,int index)
	{
		this.val=val;
		this.index=index;
		// TODO Auto-generated constructor stub
	}
	@Override
	public int compareTo(storeunit o)
	{
		// TODO Auto-generated method stub
		int cmp=val-o.val;
		if(cmp>0)
			return 1;
		else if(cmp<0)
			return -1;
		
		int cmpi=index-o.index;
		if(cmpi<0)
			return -1;
		else if(cmpi>0)
			return 1;
		
		return 0;
	}
}



----------------------------------------------------------------------------

https://discuss.leetcode.com/topic/31405/9ms-short-java-bst-solution-get-answer-when-building-bst


其他思路:从nums的最后一个元素开始一个一个地把元素插入到新数组里,每次可以二分搜索要插入的位置,要插入的位置之前的元素个数就是所求。

但是插入数组需要移动、复制,跑得慢,所以使用BST


Every node will maintain a val sum recording the total of number on it's left bottom side, dup counts the duplication. For example, [3, 2, 2, 6, 1], from back to beginning,we would have:

                1(0, 1)
                     \
                     6(3, 1)
                     /
                   2(0, 2)
                       \
                        3(0, 1)

When we try to insert a number, the total number of smaller number would be adding dup and sum of the nodes where we turn right.
for example, if we insert 5, it should be inserted on the way down to the right of 3, the nodes where we turn right is 1(0,1), 2,(0,2), 3(0,1), so the answer should be (0 + 1)+(0 + 2)+ (0 + 1) = 4

if we insert 7, the right-turning nodes are 1(0,1), 6(3,1), so answer should be (0 + 1) + (3 + 1) = 5

public class Solution {
    class Node {
        Node left, right;
        int val, sum, dup = 1;
        public Node(int v, int s) {
            val = v;
            sum = s;
        }
    }
    public List<Integer> countSmaller(int[] nums) {
        Integer[] ans = new Integer[nums.length];
        Node root = null;
        for (int i = nums.length - 1; i >= 0; i--) {
            root = insert(nums[i], root, ans, i, 0);
        }
        return Arrays.asList(ans);
    }
    private Node insert(int num, Node node, Integer[] ans, int i, int preSum) {
        if (node == null) {
            node = new Node(num, 0);
            ans[i] = preSum;
        } else if (node.val == num) {
            node.dup++;
            ans[i] = preSum + node.sum;
        } else if (node.val > num) {
            node.sum++;
            node.left = insert(num, node.left, ans, i, preSum);
        } else {
            node.right = insert(num, node.right, ans, i, preSum + node.dup + node.sum);
        }
        return node;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值