小木的算法日記-線段樹

🌳 線段樹 (Segment Tree):玩轉區間作的終極利器

你好,未來的算法大師!

想象一下,你正在處理一個巨大的數據集,比如某個電商網站一整天的用戶點擊流。老板突然問你:“下午2點到3點之間,我們的總點擊量是多少?” 幾分鐘后,他又問:“把10點到11點之間的數據,因為系統故障,全部乘以0.5,然后再告訴我下午的總點擊量。”

如果用普通的數組,每次查詢都需要遍歷,每次修改更是災難。面對這種需求,我們該怎么辦?頻繁的區間查詢和區間修改

答案,就是我們今天的主角——。線段樹 (Segment Tree)

這篇文章將帶你:

  1. 直擊痛點:理解普通數組在區間問題上的局限性。

  2. 掌握神器:了解線段樹如何用的時間復雜度解決這些問題。O(對數 N)

  3. 展望進階:一窺線段樹的“動態開點”和“懶更新”等高級玩法。

準備好了嗎?讓我們開始構建這棵神奇的“信息之樹”。


😫 The Pain: 區間問題的困境

在深入線段樹之前,我們先感受一下“沒有線段樹”時的痛苦。

假設我們有一個數組,需要頻繁查詢任意區間的最小值。一個常見的優化思路是。比如,我們可以創建一個數組,其中存儲這個后綴區間的最小值。數字預計算后綴最小后綴Min[i]nums[i:]

      # 預計算后綴最小值的示例
nums = [3, 1, 4, 2]
# suffixMin[i] 表示 nums[i..] 中的最小值
suffixMin = [0] * len(nums)  :創建一個和 nums 長度相同的數組,用來存每個位置的「后綴最小值」# 從后往前計算
suffixMin[len(nums) - 1] = nums[len(nums) - 1]  作用:最后一個人的后綴只有自己,所以他的「后綴最小值」就是自己的身高。
for i in range(len(nums) - 2, -1, -1):suffixMin[i] = min(nums[i], suffixMin[i + 1])# suffixMin 會是 [1, 1, 2, 2]
print(suffixMin)# 查詢 nums[1..] 的最小值
# O(1) 時間查詢,非常快!
print(suffixMin[1]) # 輸出 1

這種“前綴和/后綴和”思想很棒,查詢效率是,但它有一個:O(1)致命弱點

無法應對動態修改!

一旦中的某個元素改變,比如變了,那么和都需要重新計算。如果數組很大,一次修改可能導致的更新成本,這在高性能場景下是不可接受的。數字數字[1]后綴Min[0]后綴Min[1]O(N)

痛點總結

  • 靜態數組:區間查詢慢 (),單點更新快 ()。O(N)O(1)

  • 預計算數組:特定區間查詢快 (),但更新慢 ()。O(1)O(N)

我們需要一個既能快速查詢,又能快速更新的數據結構。于是,線段樹應運而生。


? The Magic: 線段樹的核心能力

一句話定義:線段樹是一種,它將一個區間分解成若干個子區間,每個節點代表一個區間的“聚合信息”(如和、最值等),從而實現快速的區間操作。平衡二叉樹

核心 API 概覽 (?)

一個基礎的線段樹通常提供以下接口:

      from typing import Callableclass SegmentTree:def __init__(self, nums: list[int], merge: Callable[[int, int], int]):"""構造函數,基于一個數組初始化線段樹。時間復雜度: O(N)Args:nums (list[int]): 原始數組。merge (Callable): 一個函數,定義了如何合并兩個子區間的信息。例如,求和是 lambda a, b: a + b,求最小值是 lambda a, b: min(a, b)。"""passdef query(self, i: int, j: int) -> int:"""查詢閉區間 [i, j] 的聚合值。時間復雜度: O(log N)"""passdef update(self, i: int, val: int):"""將數組中索引 i 的值更新為 val。時間復雜度: O(log N)"""pass

為什么是 ?O(log N)

  1. 樹高是 O(log N):線段樹在構建時,總是從中間分割區間,這保證了它是一棵的二叉樹。樹的高度決定了操作的上限。高度平衡

  2. query 的路徑:查詢一個區間 時,這個大區間可以被巧妙地拆分成樹上 個節點所代表的小區間。我們只需將這些節點的信息合并即可。[i, j]O(log N)

  3. update 的路徑:更新一個葉子節點時,我們只需要沿著從該葉子到根節點的唯一路徑,更新路徑上的所有父節點。這條路徑的長度就是樹的高度,即 。O(log N)


🚀 The Evolution: 線段樹的進階形態

基礎線段樹已經很強大了,但在更復雜的場景下,它還有一些更酷的變體。

變體名稱重要性解決的問題核心技巧
基礎線段樹?????區間查詢 & 單點更新。面試和競賽的基礎。分治思想
動態開點線段樹????處理,如 ,節省內存。超大稀疏區間[0, 10^9]不預先建樹,訪問到節點時才創建。
懶更新線段樹?????區間更新。例如,將 內所有數加 。[i, j]val將更新任務“緩存”在父節點上,需要時再下推。

懶更新 (Lazy Propagation) 舉例:
想象一下,要把 [0, 100] 這個區間的所有數都加上 5。我們不必真的去更新 101 個葉子節點。我們只需在代表 的那個根節點上貼一個標簽:“嘿,我這里的所有子孫后代都要加 5”。[0, 100]

只有當查詢操作需要深入到這個節點的子區間時,我們才把這個“懶任務”向下傳遞一層。這個技巧極大地提升了區間更新的效率,使其也能達到 。O(log N)


🧠 學習成果檢驗 (必會)

現在,檢驗一下你是否掌握了線段樹的核心思想。請嘗試回答以下問題:

問題 1:
為什么說線段樹是一種“空間換時間”的數據結構?它比原始數組多占用了多少空間?

一句話總結
線段樹就像為數組建了一個 “多層抽屜柜”,用更多抽屜(空間)換更快的查找速度(時間)。

?

詳細解釋

?
  • 原始數組:比如有 4 個蘋果(N=4),放在一排抽屜里,想找區間最小值時,得逐個翻找(O (N) 時間)。
  • 線段樹:把 4 個蘋果放進一個 3 層的抽屜柜:
    • 第 1 層:1 個抽屜(根節點),存 “所有蘋果的最小值”。
    • 第 2 層:2 個抽屜,分別存 “前 2 個蘋果” 和 “后 2 個蘋果” 的最小值。
    • 第 3 層:4 個抽屜,每個抽屜存 1 個蘋果(葉子節點)。
    • 總抽屜數:1+2+4=7 個,約是原始數組的 2 倍(實際線段樹通常開 4 倍空間,避免越界)。

空間占用結論

?
  • 線段樹需要?4 倍原始數組大小?的空間(比如 N=100,線段樹用 400 個位置),但換來每次查詢 / 更新只需 “爬 3 層樓”(O (logN) 時間),比原始數組的 “翻 100 次抽屜”(O (N))快很多!

問題 2:
如果我想用線段樹來解決“查詢區間 內有多少個偶數”的問題,我應該如何定義 中的 函數和葉子節點的值?

比喻:分糖果統計
假設每個數字是一顆糖,偶數糖是粉色(值為 1),奇數糖是藍色(值為 0)。線段樹的任務是統計任意區間內的粉色糖數量。

具體做法

  1. 葉子節點:每個葉子對應一個數字,存 “1”(粉色)或 “0”(藍色)。
    • 比如數字4(偶數)→ 葉子存1;數字3(奇數)→ 葉子存0
  2. merge 函數:父節點把左右子節點的數加起來,就像把兩堆糖合起來數總數。
    • 左子樹有 2 顆粉色糖,右子樹有 1 顆→ 父節點存 3 顆。

舉個例子
數組[2, 3, 4, 5]?→ 葉子節點是[1, 0, 1, 0]

  • 左子樹(前兩個數)的和是1+0=1(表示有 1 個偶數)。
  • 右子樹(后兩個數)的和是1+0=1
  • 根節點的和是1+1=2,即整個數組有 2 個偶數。

問題 3:
一個朋友告訴你:“我可以用 個預計算的前綴和數組,在 時間內查詢任意區間的和,并且支持 的單點更新,這比線段樹的 查詢要快!” 他的說法在什么情況下可能是合理的,又在什么情況下線段樹是更優的選擇?NO(1)O(N)O(log N)

比喻:圖書館查書 vs 改書

?
  • 朋友的方法(前綴和)

    • 提前寫好一本 “查書手冊”,記錄從第 1 頁到第 i 頁的總字數(前綴和)。
    • 查書快:想知道第 3 頁到第 5 頁的總字數,直接用手冊計算(O (1) 時間)。
    • 改書慢:如果修改第 4 頁的字數,需要重寫第 4 頁之后的所有手冊內容(O (N) 時間)。
    • 適用場景:圖書館的書很少修改(更新少),但每天有很多人查詢(查詢多)。
  • 線段樹的方法

    • 把書分成章節存進 “多層書架”,每層記錄對應章節的總字數。
    • 查書和改書都快:修改第 4 頁只需更新對應章節的書架(O (logN) 時間),查詢時逐層合并結果(O (logN) 時間)。
    • 適用場景:如果書經常被修改(比如作業答案頻繁更新),線段樹更省力。
?

結論

?
  • 朋友的方法適合 “查詢多、修改少”(如歷史數據統計)。
  • 線段樹適合 “查詢和修改都頻繁”(如實時游戲數據、動態榜單)。
    就像:
  • 查字典用目錄(前綴和)很快,但字典印好后不能改;
  • 動態更新的電子表格用線段樹結構,改一個數只影響附近區域,更靈活。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/84176.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/84176.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/84176.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Day24 元組和OS模塊

1、元組(有序 不可變 可重復) 管道工程中pipeline類接收的是一個包含多個小元組的列表作為輸入。可以這樣理解這個結構: (1) 列表 []: 定義了步驟執行的先后順序。Pipeline 會按照列表中的順序依次處理數據。之所以用列…

Auto-Coder使用GPT-4o完成:在用TabPFN這個模型構建一個預測未來3天漲跌的分類任務

通過akshare庫,獲取股票數據,并生成TabPFN這個模型 可以識別、處理的格式,寫一個完整的預處理示例,并構建一個預測未來 3 天股價漲跌的分類任務 用TabPFN這個模型構建一個預測未來 3 天股價漲跌的分類任務,進行預測并輸…

Device Mapper 機制

Device Mapper 機制詳解 Device Mapper(簡稱 DM)是 Linux 內核中的一套通用塊設備映射框架,為 LVM、加密磁盤、RAID 等提供底層支持。本文將詳細介紹 Device Mapper 的原理、實現、內核配置、常用工具、操作測試流程,并配以詳細的…

crackme006

crackme006 名稱值軟件名稱aLoNg3x.1.exe加殼方式無保護方式Serial編譯語言Delphi調試環境Win10 64位使用工具x32dbg,ida pro,PEid,DarkDe4破解日期2025-06-05 脫殼 1. 先用PEid查殼 查到無殼 尋找Serial 查詢到編程語言為Delphi 導出Delphi符號表信息到x32dbg&#xff0c…

Conda 創建新環境時報錯 HTTP 502,如何解決?

Conda 創建新環境時報錯 HTTP 502&#xff0c;如何解決&#xff1f; 最近在用 Conda 創建新環境時&#xff0c;突然遇到這樣一個錯誤&#xff1a; CondaHTTPError: HTTP 502 BAD GATEWAY for url <https://mirrors.westlake.edu.cn/ANACONDA/cloud/conda-forge/linux-64/r…

2025最全TS手寫題之partial/Omit/Pick/Exclude/Readonly/Required

隨著 TS 在工作中使用的越來越廣泛&#xff0c;面試的時候面試官也都會加上一兩個 TS 的問題來了解候選人對于 TS 的熟悉程度&#xff0c;其中就有不少手寫題目&#xff0c;比如筆者在字節的一次二面&#xff0c;面試官就問到了我如何實現一個 Pick&#xff0c;在小紅書的一面&…

基于江科大stm32屏幕驅動,實現OLED多級菜單(動畫效果),結構體鏈表實現(獨創源碼)

引言 在嵌入式系統中&#xff0c;用戶界面的設計往往直接影響到用戶體驗。本文將以STM32微控制器和OLED顯示屏為例&#xff0c;介紹如何實現一個多級菜單系統。該系統支持用戶通過按鍵導航菜單&#xff0c;執行相應操作&#xff0c;并提供平滑的滾動動畫效果。 本文設計了一個…

LLMs之StructuredOutput:大模型結構化輸出的簡介、常用方案、前沿框架之詳細攻略

LLMs之StructuredOutput&#xff1a;大模型結構化輸出的簡介、常用方案、前沿框架之詳細攻略 目錄 大模型結構化輸出的簡介 1、特點與難點 大模型結構化輸出的常用方案及對比 1、前沿框架&#xff1a;vLLM 與 XGrammar 大模型結構化輸出的案例應用 大模型結構化輸出的簡介…

Linux中shell流程控制語句

一、if條件控制 1.1 語法解讀 單路決策 - 單分支if語句樣式&#xff1a;if [ 條件 ]then指令fi特點&#xff1a;單一條件&#xff0c;只有一個輸出 雙路決策 - 雙分支if語句樣式&#xff1a;if [ 條件 ]then指令1else指令2fi特點&#xff1a;單一條件&#xff0c;兩個輸出 …

Python學習(8) ----- Python的類與對象

Python 中的類&#xff08;Class&#xff09;與對象&#xff08;Object&#xff09;是面向對象編程&#xff08;OOP&#xff09;的核心。我們可以通過“類是模板&#xff0c;對象是實例”來理解它們的關系。 &#x1f9f1; 一句話理解&#xff1a; 類就像“圖紙”&#xff0c;對…

數據結構-文件

文件是性質相同的記錄的集合。 記錄是文件中存取的基本單位&#xff0c;數據項是文件可使用的最小單位。 操作系統研究的文件是一維的無結構連續字符序列&#xff0c;數據庫中研究的文件是帶有結構的記錄集合。 文件在外存上的4種基本組織方式&#xff1a;順序、索引、散列、鏈…

前端開發面試題總結-CSS篇

文章目錄 CSS面試高頻問答1、CSS選擇器的優先級2、CSS3新特性3、如何垂直水平居中盒子4、什么是重繪和重排5、px/em/rem/vw有什么區別6、rem布局的原理7、如何設置比12px還要小的字體8、CSS中隱藏元素的方式有哪些 CSS面試高頻問答 1、CSS選擇器的優先級 2、CSS3新特性 3、如何…

Python如何給視頻添加音頻和字幕

在Python中&#xff0c;給視頻添加音頻和字幕可以使用電影文件處理庫MoviePy和字幕處理庫Subtitles。下面將詳細介紹如何使用這些庫來實現視頻的音頻和字幕添加&#xff0c;包括必要的代碼示例和詳細解釋。 環境準備 在開始之前&#xff0c;需要安裝以下Python庫&#xff1a;…

解決ubuntu20.04無法喚醒的問題的一種方法

解決ubuntu20.04無法喚醒的問題的一種方法 我更改了三個個地方&#xff0c;目前不清楚是哪個地方起的作用&#xff0c;也可能都起作用了 修改的第一個地方 步驟 1: 獲取 Swap 分區的 UUID 首先&#xff0c;你需要知道你的 swap 分區的 UUID。你可以使用以下命令來查找它&am…

【大廠機試題解法筆記】矩陣匹配

題目 從一個 N * M&#xff08;N ≤ M&#xff09;的矩陣中選出 N 個數&#xff0c;任意兩個數字不能在同一行或同一列&#xff0c;求選出來的 N 個數中第 K 大的數字的最小值是多少。 輸入描述 輸入矩陣要求&#xff1a;1 ≤ K ≤ N ≤ M ≤ 150 輸入格式 N M K N*M矩陣 輸…

Kubernetes 網絡模型深度解析:Pod IP 與 Service 的負載均衡機制,Service到底是什么?

Pod IP 的本質與特性 Pod IP 的定位 純端點地址&#xff1a;Pod IP 是分配給 Pod 網絡命名空間的真實 IP 地址&#xff08;如 10.244.1.2&#xff09;無特殊名稱&#xff1a;在 Kubernetes 中&#xff0c;它通常被稱為 “Pod IP” 或 “容器 IP”生命周期&#xff1a;與 Pod …

使用osqp求解簡單二次規劃問題

文章目錄 一、問題描述二、數學推導1. 目標函數處理2. 約束條件處理 三、代碼編寫 一、問題描述 已知&#xff1a; m i n ( x 1 ? 1 ) 2 ( x 2 ? 2 ) 2 s . t . 0 ? x 1 ? 1.5 , 1 ? x 2 ? 2.5 min(x_1-1)^2(x_2-2)^2 \qquad s.t. \ \ 0 \leqslant x_1 \leqslant 1.5,…

pe文件結構(TLS)

TLS 什么是TLS? TLS是 Thread Local Storage 的縮寫&#xff0c;線程局部存儲。主要是為了解決多線程中變量同步的問題 如果需要要一個線程內部的各個函數調用都能訪問&#xff0c;但其它線程不能訪問的變量&#xff08;被稱為static memory local to a thread 線程局部靜態變…

Electron簡介(附電子書學習資料)

一、什么是Electron&#xff1f; Electron 是一個由 GitHub 開發的 開源框架&#xff0c;允許開發者使用 Web技術&#xff08;HTML、CSS、JavaScript&#xff09; 構建跨平臺的桌面應用程序&#xff08;Windows、macOS、Linux&#xff09;。它將 Chromium瀏覽器內核 和 Node.j…

如何使用DeepSeek幫助自己的工作?(Java開發)

如何使用DeepSeek幫助自己的工作&#xff1f; 作為Java開發者&#xff0c;你可以通過以下方式高效利用DeepSeek提升工作效率&#xff08;附具體操作示例&#xff09;&#xff1a; 一、日常編碼加速 1. 代碼生成與補全 // 輸入需求描述&#xff1a; "生成SpringBoot分頁…