mirror of
https://github.com/halfrost/LeetCode-Go.git
synced 2025-07-05 16:36:41 +08:00
92 lines
2.5 KiB
Go
92 lines
2.5 KiB
Go
package leetcode
|
||
|
||
import (
|
||
"sort"
|
||
|
||
"github.com/halfrost/LeetCode-Go/template"
|
||
)
|
||
|
||
// 解法一 线段树,时间复杂度 O(n log n)
|
||
func countRangeSum(nums []int, lower int, upper int) int {
|
||
if len(nums) == 0 {
|
||
return 0
|
||
}
|
||
st, prefixSum, sumMap, sumArray, res := template.SegmentCountTree{}, make([]int, len(nums)), make(map[int]int, 0), []int{}, 0
|
||
prefixSum[0], sumMap[nums[0]] = nums[0], nums[0]
|
||
for i := 1; i < len(nums); i++ {
|
||
prefixSum[i] = prefixSum[i-1] + nums[i]
|
||
sumMap[prefixSum[i]] = prefixSum[i]
|
||
}
|
||
// sumArray 是 prefixSum 去重之后的版本,利用 sumMap 去重
|
||
for _, v := range sumMap {
|
||
sumArray = append(sumArray, v)
|
||
}
|
||
// 排序是为了使得线段树中的区间 left <= right,如果此处不排序,线段树中的区间有很多不合法。
|
||
sort.Ints(sumArray)
|
||
// 初始化线段树,节点内的值都赋值为 0,即计数为 0
|
||
st.Init(sumArray, func(i, j int) int {
|
||
return 0
|
||
})
|
||
// 倒序是为了方便寻找 j,sum(i,j) 规定了 j >= i,所以倒序遍历,i 从大到小
|
||
for i := len(nums) - 1; i >= 0; i-- {
|
||
// 插入的 prefixSum[i] 即是 j
|
||
st.UpdateCount(prefixSum[i])
|
||
if i > 0 {
|
||
res += st.Query(lower+prefixSum[i-1], upper+prefixSum[i-1])
|
||
} else {
|
||
res += st.Query(lower, upper)
|
||
}
|
||
}
|
||
return res
|
||
}
|
||
|
||
// 解法二 树状数组,时间复杂度 O(n log n)
|
||
func countRangeSum1(nums []int, lower int, upper int) int {
|
||
n := len(nums)
|
||
// 计算前缀和 preSum,以及后面统计时会用到的所有数字 allNums
|
||
allNums, preSum, res := make([]int, 1, 3*n+1), make([]int, n+1), 0
|
||
for i, v := range nums {
|
||
preSum[i+1] = preSum[i] + v
|
||
allNums = append(allNums, preSum[i+1], preSum[i+1]-lower, preSum[i+1]-upper)
|
||
}
|
||
// 将 allNums 离散化
|
||
sort.Ints(allNums)
|
||
k := 1
|
||
kth := map[int]int{allNums[0]: k}
|
||
for i := 1; i <= 3*n; i++ {
|
||
if allNums[i] != allNums[i-1] {
|
||
k++
|
||
kth[allNums[i]] = k
|
||
}
|
||
}
|
||
// 遍历 preSum,利用树状数组计算每个前缀和对应的合法区间数
|
||
bit := template.BinaryIndexedTree{}
|
||
bit.Init(k)
|
||
bit.Add(kth[0], 1)
|
||
for _, sum := range preSum[1:] {
|
||
left, right := kth[sum-upper], kth[sum-lower]
|
||
res += bit.Query(right) - bit.Query(left-1)
|
||
bit.Add(kth[sum], 1)
|
||
}
|
||
return res
|
||
}
|
||
|
||
// 解法三 暴力,时间复杂度 O(n^2)
|
||
func countRangeSum2(nums []int, lower int, upper int) int {
|
||
res, n := 0, len(nums)
|
||
for i := 0; i < n; i++ {
|
||
tmp := 0
|
||
for j := i; j < n; j++ {
|
||
if i == j {
|
||
tmp = nums[i]
|
||
} else {
|
||
tmp += nums[j]
|
||
}
|
||
if tmp <= upper && tmp >= lower {
|
||
res++
|
||
}
|
||
}
|
||
}
|
||
return res
|
||
}
|