问题:LCR 170. 交易逆序对的总数 - 力扣(LeetCode)求一个数组中逆序对的个数。
分析:该问题可以用归并排序解决,但不在本次学习分享之内。
树状数组——对于前缀和的计算,当其中一个元素改变时,与之关联的所有和全部需要重新计算,On时间复杂度。因此需要一种更有效的方法进行优化。
算法学习笔记(2) : 树状数组 - 知乎 (zhihu.com)通过阶梯状分段数组来表示部分和,具体原理可见该文章。需要额外注意的是树状数组的长度等于原数组最大元素+1,因此需要进行离散化计算:先拷贝出一个原始数组,再排序,通过二分查找标记原数组中元素在数组中大小排序情况。有由于树状数组的排好序的,因此可以方便的统计逆序对数目。
代码中lowbit中的x&-x代表x中只保留最后一个1时的数值;update代表对数组中对应区间进行更新(+1);query代表求取数组前缀和。
func lowbit(x int) int {
return x & -x
}
func update(bit []int, x, v int) {
n := len(bit)
for i := x; i < n; i += lowbit(i) {
bit[i] += v
}
}
func query(bit []int, x int) int {
sum := 0
for i := x; i > 0; i -= lowbit(i) {
sum += bit[i]
}
return sum
}
func reversePairs(nums []int) int {
n := len(nums)
if n <= 1 {
return 0
}
tmp := make([]int, n)
copy(tmp, nums)
sort.Ints(tmp)
for i := 0; i < n; i++ {
nums[i] = sort.SearchInts(tmp, nums[i]) + 1
}
bit := make([]int, n+1)
res := 0
for i := n - 1; i >= 0; i-- {
res += query(bit, nums[i]-1)
update(bit, nums[i], 1)
}
return res
}