↓快轉到主要內容
  1. LeetCode/

LeetCode 307: Range Sum Query - Mutable

·2 分鐘· ·
LeetCode Medium Segment-Tree Binary-Indexed-Tree Design Array
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: medium 第一次嘗試:2026-06-19

  • 提交紀錄:Wrong Answer → Accepted

解題思路
#

Prefix Sum 查詢 O(1),但每次 update 都要重算後面的前綴,O(n)。更新和查詢次數都到 3 × 10^4,要讓兩種操作都是 O(log n)(Segment Tree):

  • 線段樹:每個節點存一段區間的和,左右小孩各管一半。更新時往下找到葉子、回來時重算父節點;查詢時完全包含的節點直接回傳,不重疊的回傳 0,部分重疊的往下分。
  • Fenwick Tree:程式更短,update 先算差值再 add,區間和是兩個前綴相減。

我的嘗試
#

我用遞迴的線段樹。第一版的 sumRange 有兩個錯:右小孩寫成 2 * node + 1(跟左小孩一樣),而且左右兩邊都查完之後忘了 return left_sum + right_sum,部分重疊的節點回傳 None。修好就 AC 了。

正確解法
#

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

    def _build(self, nums, node, start, end):
        if start == end:
            self.tree[node] = nums[start]
            return
        mid = (start + end) // 2
        self._build(nums, 2 * node + 1, start, mid)
        self._build(nums, 2 * node + 2, mid + 1, end)
        self.tree[node] = self.tree[2 * node + 1] + self.tree[2 * node + 2]

    def update(self, index: int, val: int) -> None:
        def go(node, start, end):
            if start == end:
                self.tree[node] = val
                return
            mid = (start + end) // 2
            if index <= mid:
                go(2 * node + 1, start, mid)
            else:
                go(2 * node + 2, mid + 1, end)
            self.tree[node] = self.tree[2 * node + 1] + self.tree[2 * node + 2]
        go(0, 0, self.n - 1)

    def sumRange(self, left: int, right: int) -> int:
        def go(node, start, end):
            if right < start or end < left:
                return 0
            if left <= start and end <= right:
                return self.tree[node]
            mid = (start + end) // 2
            return go(2 * node + 1, start, mid) + go(2 * node + 2, mid + 1, end)
        return go(0, 0, self.n - 1)

複雜度
#

建樹 O(n),update 和 sumRange 各 O(log n),空間 O(n)。

要特別避免的錯誤
#

  • 左右小孩是 2 * node + 1 和 2 * node + 2,複製貼上時特別容易寫成同一個。
  • 遞迴函式每一條路徑都要有回傳值。

面試口說整理
#

會修改的區間和不能用前綴和,因為每次更新要 O(n)。我用線段樹,每個節點存它那一段的和;更新時找到葉子、回來重算祖先,查詢時完全包含的節點直接用、不重疊的回傳 0、其他的往下分。兩種操作都是 O(log n)。Fenwick Tree 也可以,程式更短。

相關文章

LeetCode 1570: Dot Product of Two Sparse Vectors
·1 分鐘
LeetCode Medium Two-Pointers Design Hash-Table Array
LeetCode 1570 解題筆記:只存非零的 (index, value),內積時用雙指針合併兩個依 index 排序的列表,index 相同才相乘
LeetCode 244: Shortest Word Distance II
·1 分鐘
LeetCode Medium Two-Pointers Design Hash-Table String Array
LeetCode 244 解題筆記:預處理每個字的出現位置(天然排序),查詢時對兩個位置列表做雙指針合併,每次移動比較小的那個
LeetCode 3719: Longest Balanced Subarray I
·1 分鐘
LeetCode Medium Hash-Table Prefix-Sum Segment-Tree Array
LeetCode 3719 解題筆記:n 只有 1500,固定左端點往右擴,用兩個集合增量維護不同的奇數和偶數
LeetCode 3960: Frequency Balance Subarray
·1 分鐘
LeetCode Medium Hash-Table Counting Array
LeetCode 3960 解題筆記:n 只有 1000,固定左端點往右擴,同時維護「值 -> 次數」和「次數 -> 個數」;只有 f 和 2f 兩種次數就成立
LeetCode 3885: Design Event Manager
·1 分鐘
LeetCode Medium Heap Design Hash-Map Lazy-Deletion
LeetCode 3885 解題筆記:dict 存目前優先度,max-heap 加 lazy deletion,彈出時再驗證
LeetCode 1229: Meeting Scheduler
·1 分鐘
LeetCode Medium Two-Pointers Sorting Array
LeetCode 1229 解題筆記:兩個人的時段各自排序後,跟 986 一樣用雙指針找交集;第一個長度夠的交集就是最早的時段