如何优化基于Fenwick Tree的最长递增子序列(LIS)解法性能?
优化Fenwick Tree实现严格递增LIS的核心方案
1. 替换冗余逻辑:用离散化去重替代路径检查+前置极小值
- 问题根源:原方案中的前置极小值和路径检查会引入额外遍历与条件判断,拖慢性能。
- 优化操作:
- 对输入数组做排序去重得到有序唯一数组,将原数组元素映射到该数组的索引(转换为Fenwick Tree的1-based索引)。
- 利用二分查找确定当前元素的查询范围:对于元素
x,用bisect_left找到其在唯一数组中的位置idx,则比x小的所有元素对应Fenwick Tree的1~idx区间,直接查询该区间最大值即可保证严格递增,无需额外路径检查。 - 移除序列头部的极小值:离散化后的索引天然适配Fenwick Tree从1开始的特性,初始状态下Fenwick Tree所有节点值为0,第一个元素查询结果为0,更新后值为1,逻辑完全自洽。
2. 优化Fenwick Tree的实现细节
- 最大值型Fenwick Tree的高效实现:
- 采用数组直接存储树结构,避免类成员函数的额外开销(若用面向对象语言,可将核心操作内联或简化)。
- 更新操作提前终止:在更新节点时,若当前节点已有值大于等于要更新的值,直接终止向上更新——因为Fenwick Tree父节点存储的是区间最大值,当前节点无变化时,父节点的最大值也不会改变,这能大幅减少循环次数。
- 查询操作极简逻辑:遍历前缀区间时仅记录最大值,避免不必要的计算。
示例代码(Python):
import bisect def get_sorted_unique(arr): sorted_arr = sorted(arr) unique = [] prev = None for num in sorted_arr: if num != prev: unique.append(num) prev = num return unique class FenwickTree: def __init__(self, size): self.n = size self.tree = [0] * (self.n + 2) # 预留额外空间避免越界 def update(self, idx, value): while idx <= self.n: if self.tree[idx] < value: self.tree[idx] = value else: break # 无需继续向上更新 idx += idx & -idx def query(self, idx): res = 0 while idx > 0: if self.tree[idx] > res: res = self.tree[idx] idx -= idx & -idx return res def strict_lis_length(arr): if not arr: return 0 sorted_unique = get_sorted_unique(arr) ft = FenwickTree(len(sorted_unique)) max_len = 0 for num in arr: idx = bisect.bisect_left(sorted_unique, num) current = ft.query(idx) + 1 ft.update(idx + 1, current) if current > max_len: max_len = current return max_len
3. 离散化的性能优化
- 避免使用
set去重:对于大规模数组,直接排序后遍历去重比先转set再排序更高效,减少哈希表的构建开销。 - 用内置高效二分实现:比如Python的
bisect模块(底层C实现),比手动写二分查找快很多。
性能对比
优化后的方案将时间复杂度稳定在O(NlogN),和线段树实现的复杂度一致,但Fenwick Tree的常数更小(代码更简洁、内存占用更低、操作步骤更少),实际运行效率会优于线段树实现。
内容的提问来源于stack exchange,提问作者hopin
相关产品推荐
相关产品推荐

