方法一:利用归并排序保存位置数组,交换时交换位置数组
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); } }