刷题使我快乐,满脸开心.jpg
- 来源:力扣(LeetCode)
- 链接:https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
- 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目
在股票交易中,如果前一天的股价高于后一天的股价
,则可以认为存在一个「交易逆序对」
。请设计一个程序,输入一段时间内的股票交易记录 record
,返回其中存在的「交易逆序对」
总数。
示例 1:
输入:record = [9, 7, 5, 4, 6]
输出:8
解释:交易中的逆序对为 (9, 7), (9, 5), (9, 4), (9, 6), (7, 5), (7, 4), (7, 6), (5, 4)。
限制:
0 <= record.length <= 50000
思路
这道题可能很多人第一反应就是双循环。思路完全正确,只可惜测试用例会让这种思路超时。。。
所以得换个新的思路:
- 既然
O(n^2)
不行,然后O(n)
显然不现实,那就试试O(n*logn)
- 从
O(n*logn)
这个复杂度,联想到了分治思想 - 尝试思考分治思想是否可行
- 直接分块处理肯定不可以
前后不同块的数据间还需要产生判断和计入结果,不可单独处理某块数据
- 归并是否可行?
前后两块数据之间的结果计算可行,单块数据内部的结果值可以递归处理,思路可行
- 具体归并思路
- 递归深入,直到元素个数
为零
或者仅1个
,这也就是递归的退出条件 - 以升序数组为例,在归并进行中,两个指针
分别指向前后
两块的第一个元素,往后逐个遍历进行归并 - 当发现
左边
元素比右边
某个元素大
时,那么就会有左边元素及其之后的所有元素
都可以和右边当前元素
形成一个逆序对,也就是可以计入结果了 - 归并完成时,逆序对个数也就统计完毕了
- 递归深入,直到元素个数
思路差不多就是这样了,细节在注释~
代码
func reversePairs(record []int) int {
// fail fast
if len(record) <= 1 {
return 0
}
var split func(i, j int) int
split = func(start, end int) int {
// 递归退出条件
if start >= end {
return 0
}
mid := (end-start)/2 + start
// 收集深层的结果
count := split(start, mid) + split(mid+1, end)
// 暂存排序结果
var temp []int
i, j := start, mid+1
for i <= mid && j <= end {
// 结果是升序排列
// 所以如果 左侧当前元素大于右侧当前元素,那么:
// 左侧当前元素及其后续元素都能形成一个逆序对
if record[i] > record[j] {
count += mid - i + 1
temp = append(temp, record[j])
j++
} else {
// 右侧元素大无法形成逆序对,‘价值’榨干,抬走下一个
temp = append(temp, record[i])
i++
}
}
// 剩余右侧元素时,说明比左侧元素大,无法形成逆序对
for ; j <= end; j++ {
temp = append(temp, record[j])
}
// 剩余左侧元素时,均能形成逆序对,但是之前计算时已经加过了,不用再加
for ; i <= mid; i++ {
temp = append(temp, record[i])
}
// 将这两段归并为升序数组,交给上层处理
for z := start; z <= end; z++ {
record[z] = temp[z-start]
}
return count
}
return split(0, len(record)-1)
}