題目:
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