leetcode:Reverse Pairs

题目大意:

  给出一个长度为n的整数数组nums,求其中逆对的数目。其中一个逆对(i, j)是指满足条件i<j且nums[i]>2*num[j]的数对。

  题目保证输入的n<=5e4。


解法:

  可以利用线段树来求解,因为其中涉及了批量区间赋值和批量区间加总。首先由于nums中数值范围是32位整数,其范围过大,无法直接使用线段树,所以先对其进行压缩。压缩的过程就是对nums进行排序得到排序完成的新数组ordered,之后从ordered中移除重复元素,之后建立与范围为[0, ordered.length - 1]的线段树,而线段树的空间复杂度为O(n)。线段树中[x, y]区间需要记录属性s,且[x, y].s = [x, x].s + [x + 1, x + 1].s + ... +[y, y].s,以及为了性能考虑需要添加一个肮脏标志[x, y].d,用于缓存对整个[x, y]区间的修改。且[x, x].s表示ordered[x]元素在nums[i]左方出现的次数,而我们需要的是对i做从0到n的迭代。每次迭代首先利用二分查找法在ordered中找到最小的元素m,使得m>nums[i]*2,记其在ordered中的下标为M,我们只需要查找线段树中区间[M, ordered.length-1].s,其表示在nums[i]左方出现的所有大于等于m的元素数目,也就是要累加到结果中的值;之后利用二分查找法找到ordered中nums[i]出现的下标P,并令线段树中区间[P, P]加1即可,这一步为之后的步骤做服务。

  时间复杂度=对nums排序的时间复杂度+从ordered中移除重复元素的时间复杂度+建立线段树的时间复杂度+n*(从线段树中查找区间s的时间复杂度+更新线段树区间s的时间复杂度+2*从ordered中二分查找元素的时间复杂度)=O(nlog2(n))+O(n)+O(n)+n*O(log2(n))=O(nlog2(n))。

  空间复杂度=额外分配的数组ordered的空间复杂度+线段树的空间复杂度=O(n)+O(n)=O(n)。


代码: 

  1 class Solution {
  2     public int reversePairs(int[] nums) {
  3         //Sort at first
  4         int[] sorted = nums.clone();
  5         Arrays.sort(sorted);
  6 
  7         //Remove duplicated element
  8         int orginalLength = sorted.length;
  9         int rpos = 1;
 10         int wpos = 1;
 11         while (rpos < orginalLength) {
 12             if (sorted[rpos] != sorted[rpos - 1]) {
 13                 sorted[wpos++] = sorted[rpos];
 14             }
 15             rpos++;
 16         }
 17 
 18         SegmentTree tree = new SegmentTree(0, wpos - 1); //tree[i] save how many guy = sorted[i] appear
 19         int result = 0;
 20         int maxAllowed = Integer.MAX_VALUE >> 1;
 21         int minAllowed = Integer.MIN_VALUE >> 1;
 22         for (int num : nums) {
 23             //x > num * 2
 24 
 25             int guyLargerThan = 0;
 26             if (num > maxAllowed) {
 27             } else if (num < minAllowed) {
 28                 guyLargerThan = tree.sumOf(0, wpos);
 29             } else {
 30                 int doubleNum = num << 1;
 31                 int halfIndex = binarySearch(sorted, 0, wpos, doubleNum);
 32                 guyLargerThan = tree.sumOf(halfIndex, wpos);
 33             }
 34             result += guyLargerThan;
 35 
 36             //Add num into tree
 37             int numIndex = binarySearch(sorted, 0, wpos, num) - 1;
 38             tree.increment(numIndex, numIndex, 1);
 39         }
 40         return result;
 41     }
 42 
 43     public static class SegmentTree {
 44         public static class SNode {
 45             int left;
 46             int right;
 47             int flag; //The incremented value for all element in [left, right]
 48             int sum; //The sum o
 49             SNode leftHalf; //[left, (left + right) / 2]
 50             SNode rightHalf; //[(left + right) / 2 + 1, right]
 51         }
 52 
 53         SNode root;
 54 
 55         public SegmentTree(int left, int right) {
 56             root = build(left, right);
 57         }
 58 
 59         private static SNode build(int left, int right) {
 60             SNode root = new SNode();
 61             root.left = left;
 62             root.right = right;
 63 
 64             int half = (left + right) >> 1;
 65 
 66             if (right > left) {
 67                 root.leftHalf = build(left, half);
 68                 root.rightHalf = build(half + 1, right);
 69             }
 70 
 71 
 72             return root;
 73         }
 74 
 75         public void increment(int left, int right, int val) {
 76             increment(root, left, right, val);
 77         }
 78 
 79         private static void increment(SNode node, int left, int right, int val) {
 80             if (node.left > right || node.right < left) {
 81                 return;
 82             }
 83             if (node.left >= left && node.right <= right) {
 84                 node.flag += val;
 85                 node.sum += val * (right - left + 1);
 86                 return;
 87             }
 88             increment(node.leftHalf, left, right, val);
 89             increment(node.rightHalf, left, right, val);
 90             node.sum = node.leftHalf.sum + node.rightHalf.sum + node.flag * (right - left + 1);
 91         }
 92 
 93         public int sumOf(int left, int right) {
 94             return sumOf(root, left, right);
 95         }
 96 
 97         private static int sumOf(SNode node, int left, int right) {
 98             left = Math.max(node.left, left);
 99             right = Math.min(node.right, right);
100             if (left > right) {
101                 return 0;
102             }
103             if (node.left >= left && node.right <= right) {
104                 return node.sum;
105             }
106             return sumOf(node.leftHalf, left, right) + sumOf(node.rightHalf, left, right)
107                     + node.flag * (right - left + 1);
108         }
109     }
110 
111     /**
112      * find the index of the first element in arr which is larger than val or end
113      *
114      * @param arr   an ordered array
115      * @param begin from which index
116      * @param end   until to which index
117      * @param val   the value to be searched
118      * @return the index of the first element in arr which is larger than val or end
119      */
120     public int binarySearch(int[] arr, int begin, int end, int val) {
121         int left = begin;
122         int right = end;
123         //arr[begin] <= val and arr[end] > val
124         while (left < right) {
125             int mid = (left + right) >> 1;
126             if (arr[mid] > val) {
127                 right = mid;
128             } else {
129                 left = mid + 1;
130             }
131         }
132         return left;
133     }
134 }
View Code

 

转载于:https://www.cnblogs.com/dalt/p/7566603.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值