315. 计算右侧小于当前元素的个数

 

 方法一:利用归并排序保存位置数组,交换时交换位置数组

class Solution {
    int[] count;
    public List<Integer> countSmaller(int[] nums) {
        int n = nums.length;
        count = new int[n];
        int[] pos = new int[n];
        for(int i = 0; i < n; i++) pos[i] = i;
        sort(nums,0,n-1,pos);
        List<Integer> res = new ArrayList<>();
        for(int num : count) res.add(num);
        return res;
    }

    public void sort(int[] nums, int left, int right, int[] pos) {
        if(left < right) {
            int mid = (left + right) >> 1;
            sort(nums,left,mid,pos);
            sort(nums,mid+1,right,pos);
            merge(nums,left,mid,right,pos);
        }
    }

    public void merge(int[] nums, int left, int mid, int right, int[] pos) {
        int[] temp = new int[right-left+1];
        int t = 0, l = left, r = mid + 1;
        while(l <= mid && r <= right) {
            if(nums[pos[l]] <= nums[pos[r]]) {
                count[pos[l]] += r - (mid + 1);
                temp[t++] = pos[l++];
            } else {
                temp[t++] = pos[r++];
            }
        }
        while(l <= mid) {
            count[pos[l]] += r - (mid + 1);
            temp[t++] = pos[l++];
        }
        while(r <= right) {
            temp[t++] = pos[r++];
        }
        System.arraycopy(temp,0,pos,left,temp.length);
    }

}

方法二:建树,节点保存一个count值,计算当前节点的左子树有多少个数(小于当前节点的值),将数组倒序插入树中,节点向当前节点右边插入时,计算小于它的个数

class Solution {
    public List<Integer> countSmaller(int[] nums) {
        int n = nums.length;
        Integer[] res = new Integer[n];
        Arrays.fill(res,0);
        Node root = null;
        for(int i = n - 1; i >= 0; i--) {
            root = insert(res,root,new Node(nums[i]),i);
        }
        return Arrays.asList(res);
    }
    public Node insert(Integer[] res, Node root, Node node, int i) {
        if(root == null) {
            root = node;
            return root;
        }

        if(root.val >= node.val) {
            root.count++;
            root.left = insert(res,root.left,node,i);
        } else {
            res[i] += root.count;
            root.right = insert(res,root.right,node,i);
        }
        return root;
    }
}
class Node {
    int val, count;
    Node left, right;
    public Node (int val) {
        this.val = val;
        count = 1;
    }
}

方法三:树状数组(不知道数据范围先离散化处理)

class Solution {
    int[] bitArr;
    int n;
    public List<Integer> countSmaller(int[] nums) {
        n = nums.length;
        bitArr = new int[n+1];
        int[] arr = nums.clone();
        Map<Integer,Integer> map = new HashMap<>();
        Arrays.sort(arr);
        int idx = 0;
        for(int num : arr) {
            if(!map.containsKey(num)) {
                map.put(num,++idx);
            }
        }
        List<Integer> res = new LinkedList<>();
        for(int i = n - 1; i >= 0; i--) {
            int x = map.get(nums[i]);
            res.add(0,count(x-1));
            add(x,1);
        }
        return res;
    }

    int lowBit(int x) {return x & (-x);}
    void add(int x, int y) {
        for(int i = x; i <= n; i += lowBit(i)) bitArr[i] += y;
    }
    int count(int x) {
        int res = 0;
        for(int i = x; i != 0; i -= lowBit(i)) res += bitArr[i];
        return res;
    }
}

方法四:线段树

class Solution {
    public List<Integer> countSmaller(int[] nums) {
        LinkedList<Integer> res = new LinkedList<>();
        int len = nums.length;
        if(len == 0) return res;
        //获取区间范围
        int start = nums[0], end = nums[0];
        for(int i = 0; i < len; i++){
            if(nums[i] < start) start = nums[i];
            if(nums[i] > end) end = nums[i];
        }
        //构建树
        SegmentTreeNode root = build(start, end);
        //从右向左,边插入边计数
        for(int i = len - 1; i >= 0; i--){
            //计数小于该元素的区间,所以要减一
            res.addFirst(count(root, start, nums[i] - 1));
            insert(root, nums[i], 1);
        }
        return res;
    }
    //线段树节点,包含左右最值和该区间叶子节点数,子区间不断递减
    private class SegmentTreeNode{
        int start, end, count;
        SegmentTreeNode left, right;

        public SegmentTreeNode(int start, int end){
            this.start = start;
            this.end = end;
            this.count = 0;
            left = null;
            right = null;
        }
    }
    //构建线段树,不断递减区间长度
    private SegmentTreeNode build(int start, int end){
        if(start > end) return null;
        SegmentTreeNode root = new SegmentTreeNode(start, end);
        if(start != end){
            int mid = start + (end - start) / 2;
            root.left = build(start, mid);
            root.right = build(mid + 1, end);
        }
        return root;
    }
    //插入并更新叶子节点
    private void insert(SegmentTreeNode root, int index, int val){

        if (root.start == index && root.end == index){
            root.count += val;
            return;
        }

        int mid = root.start + (root.end - root.start) / 2;
        if (index >= root.start && index <= mid)
            insert(root.left, index, val);
        if (index > mid && index <= root.end)
            insert(root.right, index, val);
        //更新父节点的统计数,便于正好落在区间上的查找
        root.count = root.left.count + root.right.count;
    }
    //根据区间统计
    private int count(SegmentTreeNode root, int start, int end){
        //nums[i] - 1, 排除相等的情况
        if(start > end) return 0;
        //递归到叶子节点,获取计数值
        if (start == root.start && end == root.end){
            return root.count;
        }
        int mid = root.start + (root.end - root.start) / 2;
        int leftcount = 0, rightcount = 0;
        //统计左半区
        if (start <= mid){
            if (mid < end)
                leftcount = count(root.left, start, mid);
            else
                leftcount = count(root.left, start, end);
        }
        //统计右半区
        if (mid < end){
            if (start <= mid)
                rightcount = count(root.right, mid + 1, end);
            else
                rightcount = count(root.right, start, end);
        }
        return (leftcount + rightcount);
    }
}

 

posted @ 2020-07-23 21:31  Sexyomaru  阅读(176)  评论(0编辑  收藏  举报