Given an array nums
, we call (i, j)
an important reverse pair if i < j
and nums[i] > 2*nums[j]
.
You need to return the number of important reverse pairs in the given array.
Example1:
Input: [1,3,2,3,1] Output: 2
Example2:
Input: [2,4,3,5,1] Output: 3
Note:
- The length of the given array will not exceed
50,000
. - All the numbers in the input array are in the range of 32-bit integer.
拿到题目后,最直观的解法就是遍历尝试所有的(i, j)组合,看看有多少种组合满足 i < j
and nums[i] > 2*nums[j]这一条件。
那么,写成代码就是一个嵌套的for循环:
result := 0
for i:= 0; i < len(nums); i++ { for j := i; j < len(nums); j++ {
if nums[i] > nums[j]*2 {
result += 1
} } }
可以想见,这样解法的时间复杂度为O(n²),空间复杂度为O(1)。
那么,有么有什么办法,可以把时间复杂度降低一些呢?
O(n*n)中,有个一个n是无法优化的,因为需要至少遍历每个元素一次,那么第二个n能否降低为<n的值呢?
想象一下,当我们从第0个元素往右遍历的时候,随着index的增加,我们能知道哪些信息?
(想象一个卷轴打开的样子,未打开的部分是未知的,已打开的部分就能看的内容)
对于这类顺序展开的情况,我们可以将其抽象成 f(i,j) = f(i, j-1) + c的情况。其中,可以假定f(i, j-1) 已知,我们通过解决c来完成f(i, j)的求解。
那么,在这道题目中,f(i, j-1) 和 c 分别是什么呢?
假设:
f(i, j-1) 代表 [i,j-1] 范围内,满足nums[i] > 2*nums[j] 这一条件的pair数。
c 代表以 j 为end时,一共可以新增多少个pair。
故,f(i,j) 就等于 f(i, j-1)已知的pair数 + 新增第j位后新出现的pair数
那么,如何求 c 呢?
以题目的第二个例子 [2,4,3,5,1] 举例:
nums | 2 | 4 | 3 | 5 | 1 |
index | 0 | 1 | 2 | 3 | 4 |
c | 0 | 0 | 0 | 0 | 3 |
result | 0 | 0 | 0 | 0 | 3 |
只需要数一下,[0, j-1] 范围内,有多少个元素大于 2*nums[j] 即可得到c。或者换一个角度,即统计在[0, len(nums)-1] 内大于 2*nums[j] 的元素中有多少个已经出现了。
我们可以将nums数组复制一个副本并排序,得到一个sorted[]数组,并用一个bit[]数组来标记哪些元素已经出现了(0表示元素还未出现,>0表示元素出现了若干次)。
那么,问题转变成求一个范围内的累加值,即:
idx := index(sorted, 2*nums[j]+1) //查找 2*nums[j]+1这数在sorted数组中的下标
result := sum(idx, len(sorted)-1) //计算bit[]数组 [idx, n-1] 区间的累加值
求指定范围内的累加值有几种方法:
1、for 循环遍历累加,时间复杂度O(n)
1 func rangeSum(nums []int, start, end int) int { 2 result := 0 3 for i := start; i <= end; i++ { 4 result += nums[i] 5 } 6 return result 7 }
当数组中的值有变化时,时间复杂度仍为O(0)
2、预先先算一个累计数组,即sum[];然后直接使用sum[j]-sum[i] 即可求得 [i, j]范围内的累加值。
1 var sum []int 2 func calcSums(nums []int) { 3 sum = make([]int, len(nums)) 4 lastSum := 0 5 for i := 0; i < len(nums); i++ { 6 sum[i] += nums[i] + lastSum 7 lastSum = sum[i] 8 } 9 } 10 11 func rangeSum(nums []int, start, end int) int { 12 return sum[end] - sum[start] 13 }
当数组不变时,预处理时间为O(n),取值的时间为O(1)
当数组元素变化时,需要O(n)时间来更新sum数组
3、segment tree
1 func rangeSum(nums []int, start, end int) int { 2 root := addNode(nums, 0, len(nums)-1) 3 getSum(root, start, end) 4 } 5 6 type SegmentTreeNode struct{ 7 sum int 8 start int 9 end int 10 left *SegmentTreeNode 11 right *SegmentTreeNode 12 } 13 14 func NewSegmentTreeNode(sum, start, end int, left, right *SegmentTreeNode) *SegmentTreeNode { 15 return &SegmentTreeNode{ 16 sum: sum, 17 start: start, 18 end: end, 19 left: left, 20 right: right, 21 } 22 } 23 24 func addNode(nums []int, start, end int) *SegmentTreeNode { 25 if start == end { 26 return NewSegmentTreeNode(nums[start], start, end, nil, nil) 27 } 28 mid := (start+end)>>1 29 left := addNode(nums, start, mid-1) 30 right := addNode(nums, mid+1, end) 31 return NewSegmentTreeNode(left.sum+right.sum, start, end, left, right) 32 } 33 34 func (node *SegmentTreeNode)update(idx, deltaVal int) { 35 if node.start == idx && node.end == idx { 36 node.sum += deltaValu 37 return 38 } 39 40 mid := (node.start + node.end) >> 1 41 sum := 0 42 if idx >= mid { 43 if node.right != nil { 44 node.right.update(idx, deltaVal) 45 sum += node.right.sum 46 } 47 }else{ 48 if node.left != nil { 49 node.left.update(idx, deltaValu) 50 sum += node.left.sum 51 } 52 } 53 node.sum = sum 54 } 55 56 func getSum(root *SegmentTreeNode, start, end int) int { 57 if root.start == start && root.end == end { 58 return root.sum 59 } 60 //all in left subtree 61 if end < root.start { 62 if root.left == nil { 63 return 0 64 } 65 return getSum(root.left, start ,end) 66 } 67 //all in right subtree 68 if start > root.end { 69 if root.right == nil { 70 return 0 71 } 72 return getSum(root.right, start ,end) 73 } 74 leftSum := getSum(root.left, start, root.start) 75 rightSum := getSum(root.right, root.right, end) 76 return leftSum+root.sum+rightSum 77 }
segment tree的缺点是无法改变数组长度。
因为,为了保证查询时间是logn,因此在构造时使用了二分法保证树尽量平衡。
4、fenwick tree
也称为 Binary Index Tree
1 func reversePairs(nums []int) int { 2 sorted := getSortedArray(nums) 3 fenwickTree := NewFenwickTree(len(nums)+1) 4 result := 0 5 for i := 0; i < len(nums); i++{ 6 if 2*nums[i]+1 <= sorted[len(sorted)-1] { 7 idx := index(sorted, 2*nums[i]+1) 8 count := fenwickTree.getSum(len(fenwickTree.arr)-1) - fenwickTree.getSum(idx-1) 9 result += count 10 fmt.Println(i, nums[i], idx, count, result, fenwickTree.arr) 11 } 12 //将nums[i]加入到fenwick tree中,让后面的元素可以看到它 13 fenwickTree.update(index(sorted, nums[i]), 1) 14 } 15 return result 16 } 17 18 type FenwickTree struct { 19 arr []int 20 } 21 func NewFenwickTree(n int) *FenwickTree { 22 return &FenwickTree{arr: make([]int, n)} 23 } 24 func (ft *FenwickTree)update(idx, delta int) { 25 for ;idx < len(ft.arr); { 26 ft.arr[idx] += delta 27 idx += idx & -idx 28 } 29 //fmt.Println(idx, ft.arr) 30 } 31 func (ft *FenwickTree)getSum(idx int) int { 32 sum := 0 33 for ;idx > 0; { 34 sum += ft.arr[idx] 35 idx -= idx &-idx 36 } 37 return sum 38 } 39 40 func index(sorted []int, val int) int { 41 l, r, m := 0, len(sorted)-1, 0 42 for ; l < r; { 43 m = (l+r) >> 1 44 if sorted[m] == val { 45 l = m 46 break 47 }else if sorted[m] > val{ 48 r = m-1 49 }else{ 50 l = m+1 51 } 52 } 53 //fmt.Println(val, l+1) 54 return l+1 55 } 56 57 func getSortedArray(nums []int) []int { 58 list := make([]int, len(nums)) 59 for i := 0; i < len(nums); i++ { 60 list[i] = nums[i] 61 } 62 63 sort.Slice(list, func (i,j int) bool{ 64 return list[i]<list[j] 65 }) 66 return list 67 }
Reference:
https://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/
https://zh.wikipedia.org/zh-hans/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84