You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归线段树是否比迭代线段树占用更多空间?实现疑问

关于线段树2n空间递归实现的疑问

我正在学习线段树(segment tree)数据结构。见过不少仅用2n空间的迭代式线段树,于是尝试把同样的构建方法用到带递归update和sumRange的线段树上,但没能成功。想请教:

  • 这种做法是否不可行?
  • 为什么迭代式线段树能用2n空间存储,递归式却需要4n?
  • 还是我的实现有缺陷?

我尝试的2n空间版本用的是1-indexed树,tree[0]不存数据,根节点在tree[1]。递归调用时初始范围设为1到n-1,但不确定对不对:改成到self.n、从0开始,或是传递index+1、left+1、right+1时,都会出现不同的错误结果。

尝试的2n空间递归实现

from typing import List

class NumArray:
    # Classic Segment Tree

    def __init__(self, nums: List[int]):
        self.n = len(nums)
        self.tree = [0] * self.n * 2
        self.build(nums)

    def build(self, nums):
        # leaves
        for i in range(self.n):
            self.tree[i + self.n] = nums[i]

        # internal
        for i in range(self.n - 1, 0, -1):
            self.tree[i] = self.tree[i * 2] + self.tree[i * 2 + 1]

    def merge(self, left, right):
        return left + right

    def _update(self, tree_idx, seg_left, seg_right, i, val):
        # leaf
        if seg_left == seg_right:
            self.tree[tree_idx] = val
            return

        mid = (seg_left + seg_right) // 2
        if i > mid:
            self._update(tree_idx * 2 + 1, mid + 1, seg_right, i, val)
        else:
            self._update(tree_idx * 2, seg_left, mid, i, val)

        self.tree[tree_idx] = self.merge(self.tree[tree_idx * 2], self.tree[tree_idx * 2 + 1])

    def update(self, index: int, val: int) -> None:
        self._update(1, 1, self.n - 1, index, val)

    def _sumRange(self, tree_idx, seg_left, seg_right, query_left, query_right):
        # segment out of query bounds
        if seg_left > query_right or seg_right < query_left:
            return 0

        # segment fully in bounds
        if seg_left >= query_left and seg_right <= query_right:
            return self.tree[tree_idx]

        # segment partially in bounds
        mid = (seg_left + seg_right) // 2

        # 优化:仅必要时遍历单侧子树
        if query_left > mid:
            return self._sumRange(tree_idx * 2 + 1, mid + 1, seg_right, query_left, query_right)
        elif query_right <= mid:
            return self._sumRange(tree_idx * 2, seg_left, mid, query_left, query_right)

        left_sum = self._sumRange(tree_idx * 2, seg_left, mid, query_left, query_right)
        right_sum = self._sumRange(tree_idx * 2 + 1, mid + 1, seg_right, query_left, query_right)

        return self.merge(left_sum, right_sum)

    def sumRange(self, left: int, right: int) -> int:
        return self._sumRange(1, 1, self.n - 1, left, right)

可正常运行的4n空间递归实现

from typing import List

class NumArray:
    # Classic Segment Tree
    # 0-indexed recursive

    def __init__(self, nums: List[int]):
        self.n = len(nums)
        self.tree = [0] * self.n * 4
        self.build(nums, 0, 0, self.n - 1)

    def build(self, nums, tree_idx, left, right):
        # leaf
        if left == right:
            self.tree[tree_idx] = nums[left]
            return

        mid = (left + right) // 2
        self.build(nums, tree_idx * 2 + 1, left, mid)
        self.build(nums, tree_idx * 2 + 2, mid + 1, right)

        self.tree[tree_idx] = self.tree[tree_idx * 2 + 1] + self.tree[tree_idx * 2 + 2]

    def merge(self, left, right):
        return left + right

    def _update(self, tree_idx, seg_left, seg_right, i, val):
        # leaf
        if seg_left == seg_right:
            self.tree[tree_idx] = val
            return

        mid = (seg_left + seg_right) // 2
        if i > mid:
            self._update(tree_idx * 2 + 2, mid + 1, seg_right, i, val)
        else:
            self._update(tree_idx * 2 + 1, seg_left, mid, i, val)

        self.tree[tree_idx] = self.merge(self.tree[tree_idx * 2 + 1], self.tree[tree_idx * 2 + 2])

    def update(self, index: int, val: int) -> None:
        self._update(0, 0, self.n - 1, index, val)

    def _sumRange(self, tree_idx, seg_left, seg_right, query_left, query_right):
        # segment out of query bounds
        if seg_left > query_right or seg_right < query_left:
            return 0

        # segment fully in bounds
        if seg_left >= query_left and seg_right <= query_right:
            return self.tree[tree_idx]

        # segment partially in bounds
        mid = (seg_left + seg_right) // 2

        # 优化:仅必要时遍历单侧子树
        if query_left > mid:
            return self._sumRange(tree_idx * 2 + 2, mid + 1, seg_right, query_left, query_right)
        elif query_right <= mid:
            return self._sumRange(tree_idx * 2 + 1, seg_left, mid, query_left, query_right)

        left_sum = self._sumRange(tree_idx * 2 + 1, seg_left, mid, query_left, query_right)
        right_sum = self._sumRange(tree_idx * 2 + 2, mid + 1, seg_right, query_left, query_right)

        return self.merge(left_sum, right_sum)

    def sumRange(self, left: int, right: int) -> int:
        return self._sumRange(0, 0, self.n - 1, left, right)

问题解答

1. 2n空间的递归线段树完全可行,你的实现存在索引匹配错误

你尝试的版本核心问题是递归时的区间范围和数组索引不匹配:

  • 线段树叶子节点tree[n]到tree[2n-1]对应原数组的nums[0]到nums[n-1](0-indexed);
  • 但递归调用update和sumRange时,初始区间设为(1, self.n-1),错误地把原数组当成1-indexed处理,导致索引错位。

修复方法:将递归初始调用的区间改为原数组的0-indexed范围:

def update(self, index: int, val: int) -> None:
    self._update(1, 0, self.n - 1, index, val)

def sumRange(self, left: int, right: int) -> int:
    return self._sumRange(1, 0, self.n - 1, left, right)

修改后,2n空间的递归实现即可正常工作。

2. 为什么迭代式用2n,传统递归式用4n?

这两种线段树的结构设计不同:

  • 2n空间线段树:是固定结构的完全二叉树,叶子节点层直接对应原数组(长度不足2的幂时会补0),父节点可通过编号直接计算子节点(2*i和2*i+1),空间利用紧凑,天然只需要2n空间,迭代和递归都能复用该结构;
  • 4n空间线段树:是自适应结构的二叉树,递归构建时根据实际区间划分左右子树,不需要补全到2的幂,为避免数组越界,通常用4n作为保守的安全空间(理论最坏情况需要2n,4n是简化实现的通用选择)。

内容的提问来源于stack exchange,提问作者Alec

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 21:22:03