- 線段樹(segment tree)又稱「區間樹」,是一個高級數據結構,應用的對象是「數組」;
- 線段樹是一種實現了高效的「區間查詢」與「區間更新」的數據結構。
前置知識:理解「前綴和」數組
-
preSum[i]
表示nums[0..i - 1]
裏全部元素的和(一個數代表了原始數組的一個前綴區間的和); -
前綴和數組,就是一堆前綴和,可以用於:快速計算區間和(查詢區間和 );
-
區間
[i..j]
的和:preSum[j + 1] - preSum[i]
。
「前綴和數組」與「線段樹」都是原始數組的預處理數組
-
有了前綴和數組,就可以把原始數組丟棄了;
-
有了線段樹(數組),也可以把原始數組丟棄了。
可以認爲都是對原始數組的預處理數組,把一些需要用到的值提前計算出來,思想:空間換時間。
例題:「力扣」第 303 題:區域和檢索 - 數組不可變
- 題目鏈接:303. 區域和檢索 - 數組不可變
分析:
- 我們可以設計一個前綴和數組,在查詢區間和的時候,只用 時間複雜度,不過在數組元素有頻繁更新的時候,會導致性能下降,即這種方式不適用於「力扣」第 307 題;
- 缺點:前綴和數組,在有更新需求的前提下,不能高效地工作。
Java 代碼:
public class NumArray {
// 前綴和思想:注意:有一個單位的偏移
private int[] preSum;
public NumArray(int[] nums) {
int len = nums.length;
preSum = new int[len + 1];
for (int i = 0; i < len; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
}
public int sumRange(int i, int j) {
return preSum[j + 1] - preSum[i];
}
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4};
NumArray numArray = new NumArray(nums);
int result = numArray.sumRange(2, 3);
System.out.println(result);
}
}
/**
* Your NumArray object will be instantiated and called as such:
* NumArray obj = new NumArray(nums);
* int param_1 = obj.sumRange(i,j);
*/
Python 代碼:
from typing import List
class NumArray:
def __init__(self, nums: List[int]):
"""
:type nums: List[int]
"""
self.size = len(nums)
if self.size == 0:
return
self.pre_sum = [0 for _ in range(self.size + 1)]
self.pre_sum[0] = 0
for i in range(self.size):
self.pre_sum[i + 1] = self.pre_sum[i] + nums[i]
def sumRange(self, i: int, j: int) -> int:
if self.size > 0:
return self.pre_sum[j + 1] - self.pre_sum[i]
return 0
# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# param_1 = obj.sumRange(i,j)
「力扣」第 307 題:區域和檢索 - 數組可修改
- 題目鏈接:307. 區域和檢索 - 數組可修改。
分析:
- 如果我們不使用任何數據結構,每次求「區間和」,都會遍歷這個區間裏的所有元素。如果區間裏包含的元素很多,並且查詢次數很頻繁,時間複雜度是 ;
- 如果使用前綴和,更新操作的時間複雜度是 ;
- 如果我們使用線段樹,就可以把時間複雜度降低到 。
這裏要注意的是「線段樹」解決的區間問題不涉及「添加」與「刪除」操作。即「CURD」,只負責「U」 和 「R」。
使用「遍歷」與使用「線段樹」對於「區間更新」與「區間查詢」操作的複雜度
遍歷 | 線段樹 | |
---|---|---|
區間查詢 | ||
區間更新 |
說明:由於我們的線段樹(區間樹)採用平衡二叉樹實現,因此 中的對數函數以 2 爲底,即 。
總結
線段樹只回答了以下兩個問題,不回答區間裏有「刪除」和「添加」操作的場景。
- 區間和查詢
- 單點(區間)更新