leetcode 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:

Input: [5,2,6,1]
Output: [2,1,1,0] 
Explanation:
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.

 

=================================2018.12.30=============================

lintcode 249.

249. 统计前面比自己小的数的个数

给定一个整数数组(下标由 0 到 n-1, n 表示数组的规模,取值范围由 0 到10000)。对于数组中的每个 ai 元素,请计算 ai前的数中比它小的元素的数量。

样例

对于数组[1,2,7,8,5] ,返回 [0,1,2,3,2]

说明

在做此题前,最好先完成以下三道题: 线段树的构造, 线段树的查询 II,和 比给定数小的项目数 I 。

注意事项

We suggest you finish problem Segment Tree BuildSegment Tree Query II and Count of Smaller Number first.

public class Solution {
    /**
     * @param A: an integer array
     * @return: A list of integers includes the index of the first number and the index of the last number
     */
     class TreeNode{
         int val;
         int cnt=1;
         TreeNode left;
         TreeNode right;
         int lnum;
         TreeNode(int val){
             this.val=val;
         }
     }
    public List<Integer> countOfSmallerNumberII(int[] A) {
        List<Integer> res=new ArrayList<Integer>();
        if(A.length==0) return res;
        TreeNode root=new TreeNode(A[0]);
        res.add(0);
        for(int i=1;i<A.length;i++){
            int c=0;
            TreeNode cur=root;
            int a=A[i];
            while(true){
                if(cur.val<a){
                    c+=cur.lnum+cur.cnt;
                    if(cur.right==null){
                        cur.right=new TreeNode(a);
                        break;
                    }
                    cur=cur.right;
                }else if(cur.val>a){
                    cur.lnum++;
                    if(cur.left==null){
                        cur.left=new TreeNode(a);
                        break;
                    }
                    cur=cur.left;
                }else{
                    c+=cur.lnum;
                    cur.cnt++;
                    break;
                }
            }
            res.add(c);
        }
        return res;
    }
}

========================================================================

这题O(n^2)的方法容易,既然要减少时间复杂度。考虑以空间换时间。网上有多种思路,这里是构造一个二叉查找树,遍历的时候,再把数据插入到BST。在根据返回值计算出小于当前值得个数。

class Solution {
    //类似LinkedHashMap数据在双向链表中存了一份,以空间为代价,为查找提供便利
    class Node{
        int val;
        int times=1;
        int small;//所有左边子节点的总数
        Node left;
        Node right;
        Node(int small,int val){
            this.small=small;
            this.val=val;
        }
    }
    public List<Integer> countSmaller(int[] nums) {
        List<Integer> list=new LinkedList<>();//插入用linkedList更快
        if(nums==null||nums.length==0) return list;
        Node root=new Node(0,nums[nums.length-1]);
        list.add(0,0);
        for(int i=nums.length-2;i>=0;i--){
            Node node=new Node(0,nums[i]);
            list.add(0,insert(root,node));
        }
        return list;
    }
    //每次更新small,返回关于某节点的子节点 小于新节点的累加和
    public int insert(Node root,Node node){
        int count=0;
        if(root.val>node.val){
            root.small++;
            if(root.left==null){
                root.left=node;
            }else{
                count=insert(root.left,node);
            }
            return count;
        }else if(root.val<node.val){
            if(root.right==null){
                root.right=node;
            }else{
                count=insert(root.right,node);
            }
            return count+root.small+root.times;//算上当前结点
        }
        root.times++;
        return root.small;//相等
    }
}

还有几种高级数据结构的做法待改进。。。

 

分治法。

思路:从大到小合并左右已排序的两段,根据右边剩余,来个左边元素对应的counts[ ],但是,排序后元素的位置要变化,就不能追踪某个元素的逆序数和其开始对应的位置了。这就自然想到一个数组pos[ ]排名和开始的位置建立联系。

此方法类似归并排序,但不用创建新数组就行排序,只在pos[ ]更新排名和位置的信息即可,参考https://blog.csdn.net/jmspan/article/details/51219203

 1.类似归并排序,因为归并排序要改变元素位置,无法追踪每个元素对应的值,
 2.每次排序记录一个排名和位置的映射,pos[i]=j排名第i对应原位置j,能通过位置j在找到原值nums[j],j能代表原值。
 3.每次归并时,只是对应两个已排序的区间,pos[i]记录这个区间的排序信息,不用真的定义一个数组排序。

时间复杂度O(nlog n)。

 public List<Integer> countSmaller(int[] nums) {
        List<Integer> list=new ArrayList<>();
        if(nums==null||nums.length==0) return list;
        int[] pos=new int[nums.length];
        int[] counts=new int[nums.length];
        for(int i=0;i<nums.length;i++) pos[i]=i;//排名的初始化赋值
        sort(nums,counts,pos,0,nums.length-1);
        for(int count:counts) {
            list.add(count);
        }
        return list;
    }
    public void sort(int[] nums,int[] counts,int[] pos,int l,int r){
        if(l==r) return;
        int m=(l+r)/2;
        sort(nums,counts,pos,l,m);
        sort(nums,counts,pos,m+1,r);
        //统计count根据右边那个子数组剩余,更新pos[],根据[l,m]区间从大到小
        int[] temp=new int[r-l+1];//总数组的新排名
        int lc=l,rc=m+1,t=0;
        while(lc<=m&&rc<=r){
            if(nums[pos[lc]]<nums[pos[rc]]){
                temp[t++]=pos[rc++];
            }else if(nums[pos[lc]]>nums[pos[rc]]){
                //更新counts,因为左边大于右边,是逆序,加上右边剩余的数,右边顺序已经打乱,但是在一个子区间内。
                counts[pos[lc]]+=(r-rc+1);//pos[lc]:排名和位置的映射
                temp[t++]=pos[lc++];
            }else{//相等先存右边
                temp[t++]=pos[rc++];
            }
        }
        //剩余元素存进新排名
        while(lc<=m) temp[t++]=pos[lc++];
        while(rc<=r) temp[t++]=pos[rc++];   
        //新一轮的位置信息存在了temp中,更新pos
        for(int i=0;i<temp.length;i++) pos[l+i]=temp[i];
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值