这题 和 327. Count of Range Sum and 493. Reverse Pairs 几乎一样的解法,都属于hard 题, 可以用 merge sort, segment tree 和 BST 去求解。 注意如果自己去实现简单的BST, 在数组已经排序好的情况下 比 [1 2 3 4 5 6 ],会造成BST 退化成一个数组,从而造成TLE, 可以去用TreeSet 去做,TreeSet 用红黑树实现,从而避免BST退化。
这里先介绍merge sort 解法:
题目大意: 需要统计数组中每个数 右边小于这个数的数字的个数。
Example: Input: [5,2,6,1] Output: [2,1,1,0]
既然是统计右边 需要不改变 统计过程中的相对位置, 那么merge sort 可以尝试,因为每次merge 过程中, 在“merge 前” 两个子数组中右边的index 一定比左边的index 小。
这篇文章详细介绍了merge sort 的分解过程 https://www.cnblogs.com/DSNFZ/articles/7745785.html。
详细算法:
1. [5] [2] [6] [1] 先被分解成四个 自数组, 然后开始两两merge:
2. [5] and [2], left side [5] 的index 一定比 right [2] 的小, 因为 2< 5, 所以 得到关系count(5) =1 , count(2) = 0 然后 merge 之后变成 [2 ,5]
3. 同step 2, merge [6] [1] ,先做merge 之前的关系统计, 得到 [1(6), 0(1) ] , merge 之后变成 [1 6 ]
4. 然后merge [ 2 5] 和 [1 6] ,注意此时 left [2 5] 的index 都比 [ 1 6] 小, 虽然 [2 5 ] 和[1 6] 的内部 index 关系已经被破坏,但已经在 2, 3 两步统计出 他们之前的关系 count(5) =1 count(2) =0, count(1) = 0 count(6) = 1
因为 left 2> right 1, 所以 2的count 变成1 , count(2) = 1, 然后 left 5> right 1 所以 count(5) ++ = 2, 这样得到 所有 4个数之间的关系 count(1) = 0, count(2) =1, count(2) =1, count(6) = 1 然后merge [2 5] 和[1 6] 得到最终Merge 后的数组 [1 2 5 6 ]
5. 至此算法结束,值得注意的时, 要求输出的结果是 没排序之前的 关系,然后经过merge sort 后 数组已经排好序了。 这里一个小trick, 本质上也是hash table 思想: 定义一个int[len] [2] nums_counts, 每个[2] 定义成 [num[i],i ] 这样不管怎么排序, i都表示最初的index, 然后把需要统计的 counts 单独 定义一个数组 int[] counts, 每次统计 counts 关系 时 都 用 counts[nums_count[1]] 来记录没排序前数组的统计。
class Solution { public List<Integer> countSmaller(int[] nums) { if(nums == null || nums.length==0) return new ArrayList<Integer>(); int[][] nums_count = new int[nums.length][2]; // [0] = nums[i], [1] = i, 因为返回结果需要和排序前一样,所以把nums[i]和i 合并一起。 int[] counts = new int[nums.length]; for(int i=0; i<nums.length; i++){ nums_count[i][0] = nums[i]; nums_count[i][1] = i; } merge_count(nums_count, 0,nums_count.length-1,counts); List<Integer> result = new ArrayList<>(); for(int count: counts){ result.add(count); } return result; } private void merge_count(int[][] nums, int s, int e, int[] counts){ if(s<e){ int mid = (s+e)/2; merge_count(nums,s,mid,counts); merge_count(nums,mid+1,e,counts); //[2,5] [1,6] int j = mid+1; for(int i=s; i<=mid; i++){ while(j<=e && nums[i][0] >nums[j][0]) j++; counts[nums[i][1]] += j-(mid+1); } merge(nums,s,mid,e); } } private void merge(int[][] nums, int s, int mid, int e){ int[][] tmp = new int[e-s+1][2]; int i=s, j = mid+1; int k = 0; while(i<=mid && j<=e){ if(nums[i][0] <= nums[j][0]) { tmp[k][0] = nums[i][0]; tmp[k][1] = nums[i][1]; //每次在交换 [0] 时, 需要把[1] 表示的 index 同时交换,保证, nums[i] 和index 永远在一起。 k++; i++; } else { tmp[k][0] = nums[j][0]; tmp[k][1] = nums[j][1]; k++; j++; } } while(i<=mid) { tmp[k][0] = nums[i][0]; tmp[k][1] = nums[i][1]; k++; i++; } while(j<=e){ tmp[k][0] = nums[j][0]; tmp[k][1] = nums[j][1]; k++; j++; } int t = s; for(int[] num: tmp){ nums[t][0] = num[0]; nums[t][1] = num[1]; t++; } } }