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://leetcode.com/problems/count-of-smaller-numbers-after-self/
题解:
一开始没有什么想法,后来看tag有segment tree,正好前面也做过于是撸了一棵出来,结果碰到一个超常的数据会超时。又试了一下简单的Brute Force,也会超时。最后还是去Discuss区观摩大神们,发现有好些解法,比如以下
- 利用Merge Sort count Inversion: https://leetcode.com/discuss/73256/mergesort-solution
- Binary Search Tree: https://leetcode.com/discuss/73280/my-simple-ac-java-binary-search-code
- Building BST : https://leetcode.com/discuss/73762/9ms-short-java-bst-solution-get-answer-when-building-bst
- Segment Tree: https://leetcode.com/discuss/73233/complicated-segmentree-solution-hope-to-find-a-better-one
- Fenwich Tree (<- fastest I've seen) : https://leetcode.com/discuss/74961/7ms-java-solution-using-binary-indexed-tree
- Bit comparision : https://leetcode.com/discuss/74994/nlogn-divide-and-conquer-java-solution-based-bit-comparison
下面代码是参考yavinci大神的,从右向左遍历数组并且构建BST,当前节点node左侧全部是值小于或者等于当前节点val的节点,当前结点node.count就是他们的和。而每次addNode假如发现逆序,则可以取当前节点的count值返回。 一个小地方是,把结果全部加入到List<Integer>里,最后再reverse这个list,要比每次list.add(0, count)速度要快很多。这个算法worst case time complexity其实还是O(n2),要有AVL Tree才能缩短到O(nlogn)。 二刷还是要研究一下merge sort的解法。 比较难的Fenwick Tree解法代码很简单,速度也最快,也留给以后再研究了。
Time Complexity - O(n2), Space Complexity - O(n)
public class Solution { private class TreeNode { public int val; public int count = 1; public TreeNode left, right; public TreeNode(int val) { this.val = val; } } public List<Integer> countSmaller(int[] nums) { List<Integer> res = new ArrayList<>(); if(nums == null || nums.length == 0) { return res; } TreeNode root = new TreeNode(nums[nums.length - 1]); res.add(0); for(int i = nums.length - 2; i >= 0; i--) { int count = addNode(root, nums[i]); res.add(count); } Collections.reverse(res); return res; } private int addNode(TreeNode root, int val) { int curCount = 0; while(true) { if(val <= root.val) { root.count++; // add the inversion count if(root.left == null) { root.left = new TreeNode(val); break; } else { root = root.left; } } else { curCount += root.count; if(root.right == null) { root.right = new TreeNode(val); break; } else { root = root.right; } } } return curCount; } }
Segment Tree: 超时TLE
Time Complexity - O(n2), Space Complexity - O(n)
public class Solution { private class SegmentTreeNode { public int start; public int end; public int max; public int min; public SegmentTreeNode left, right; public SegmentTreeNode(int start, int end) { this.start = start; this.end = end; } } private SegmentTreeNode root; public List<Integer> countSmaller(int[] nums) { List<Integer> res = new ArrayList<>(); if(nums == null || nums.length == 0) { return res; } root = buildTree(nums, 0, nums.length - 1); for(int i = 0; i < nums.length; i++) { res.add(countSmaller(root, nums[i], i + 1, nums.length - 1)); } return res; } private int countSmaller(SegmentTreeNode node, int target, int lo, int hi) { // search position to nums if(node == null) { return 0; } int mid = node.start + (node.end - node.start) / 2; if(node.start == lo && node.end == hi) { if(node.max < target) { return hi - lo + 1; } else if(node.min > target) { return 0; } else { return countSmaller(node.left, target, lo, mid) + countSmaller(node.right, target, mid + 1, hi); } } else if (lo > mid) { return countSmaller(node.right, target, lo, hi); } else { return countSmaller(node.left, target, lo, mid) + countSmaller(node.right, target, mid + 1, hi); } } private SegmentTreeNode buildTree(int[] nums, int start, int end) { if(start > end) { return null; } SegmentTreeNode node = new SegmentTreeNode(start, end); if(start == end) { node.max = nums[start]; node.min = nums[start]; } else { int mid = start + (end - start) / 2; node.left = buildTree(nums, start, mid); node.right = buildTree(nums, mid + 1, end); node.max = Math.max(node.left != null ? node.left.max : Integer.MIN_VALUE, node.right != null ? node.right.max : Integer.MIN_VALUE); node.min = Math.min(node.left != null ? node.left.min : Integer.MAX_VALUE, node.right != null ? node.right.min : Integer.MAX_VALUE); } return node; } }
Brute Force: TLE超时
Time Complexity - O(n2), Space Complexity - O(n)
public class Solution { public List<Integer> countSmaller(int[] nums) { List<Integer> res = new ArrayList<>(); if(nums == null || nums.length == 0) { return res; } for(int i = 0; i < nums.length; i++) { int count = 0; for(int j = i + 1; j < nums.length; j++) { if(nums[j] < nums[i]) { count++; } } res.add(count); } return res; } }
Reference:
https://leetcode.com/discuss/73256/mergesort-solution
https://leetcode.com/discuss/73280/my-simple-ac-java-binary-search-code
https://leetcode.com/discuss/73509/nlogn-time-space-mergesort-solution-with-detail-explanation
https://leetcode.com/discuss/73762/9ms-short-java-bst-solution-get-answer-when-building-bst
https://leetcode.com/discuss/74110/11ms-java-solution-using-merge-sort-with-explanation
https://leetcode.com/discuss/73233/complicated-segmentree-solution-hope-to-find-a-better-one
https://leetcode.com/discuss/74994/nlogn-divide-and-conquer-java-solution-based-bit-comparison
https://leetcode.com/discuss/73917/accepted-c-solution-using-segment-tree
https://leetcode.com/discuss/73803/easiest-java-solution
https://leetcode.com/discuss/74961/7ms-java-solution-using-binary-indexed-tree
https://leetcode.com/discuss/73739/java-o-nlogn-solution-using-bst-with-size-information
https://leetcode.com/discuss/73558/15-ms-accepted-java-soln-using-mergesort
https://www.topcoder.com/community/data-science/data-science-tutorials/binary-indexed-trees/#prob