【LeetCode 熱題 100】105. 從前序與中序遍歷序列構造二叉樹——(解法二)O(n)

Problem: 105. 從前序與中序遍歷序列構造二叉樹
給定兩個整數數組 preorder 和 inorder ,其中 preorder 是二叉樹的先序遍歷, inorder 是同一棵樹的中序遍歷,請構造二叉樹并返回其根節點。

【LeetCode 熱題 100】105. 從前序與中序遍歷序列構造二叉樹——(解法一)O(n^2)

文章目錄

  • 整體思路
  • 完整代碼
  • 時空復雜度
    • 時間復雜度:O(N)
    • 空間復雜度:O(N)

整體思路

這段代碼同樣旨在解決 “從前序與中序遍歷序列構造二叉樹” 的問題。與上一個通過復制子數組的解法相比,此版本采用了哈希表指針/索引來界定子數組范圍,從而極大地優化了時空效率,是解決此問題的標準最優解法。

算法的核心思想依然是基于前序和中序遍歷性質的分治法,但實現細節完全不同:

  1. 預處理:哈希表加速查找

    • 算法首先創建一個哈希表 index,用于存儲中序遍歷中每個元素值及其對應的索引。
    • 目的:這一步是性能優化的關鍵。它將“在中序遍歷中查找根節點位置”這個操作的時間復雜度從 O(N) 降到了 O(1)
  2. 通過索引定義子問題

    • 算法不再通過 Arrays.copyOfRange 來創建新的子數組,而是定義了一個輔助的 dfs 函數,它接收多個索引參數來界定當前需要處理的子數組范圍。
    • dfs(preorder, preL, preR, inL, inR, index):
      • preL, preR: 定義了當前子樹在前序遍歷數組 preorder 中的范圍 [preL, preR) (左閉右開)。
      • inL, inR: 定義了當前子樹在中序遍歷數組 inorder 中的范圍 [inL, inR)
    • 這種方式避免了任何數組復制,所有操作都在原始數組上進行。
  3. 遞歸構造邏輯

    • dfs 函數的執行流程如下:
      a. 基線條件if (preL == preR),如果范圍為空,說明子樹為空,返回 null
      b. 確定根節點:當前子樹的根節點值是 preorder[preL]
      c. 分割子樹 (O(1) 操作)
      • 使用預處理好的哈希表 index.get(preorder[preL]),瞬間(O(1)時間)找到根節點在中序遍歷中的位置,記為 inRootIndex
      • 計算左子樹的大小 leftSize = inRootIndex - inL
        d. 遞歸構建左右子樹
      • 左子樹
        * 前序范圍是 [preL + 1, preL + 1 + leftSize)
        * 中序范圍是 [inL, inL + leftSize)
        * 遞歸調用 dfs(...) 構建左子節點 left
      • 右子樹
        * 前序范圍是 [preL + 1 + leftSize, preR)
        * 中序范圍是 [inL + 1 + leftSize, inR)
        * 遞歸調用 dfs(...) 構建右子節點 right
        e. 合并結果:創建新的 TreeNode,連接上一步構造出的 leftright 子節點,并返回。

通過哈希表和索引范圍,該算法將每一步遞歸中的核心操作都優化到了 O(1),從而實現了整體的線性時間復雜度。

完整代碼

class Solution {/*** 根據前序遍歷和中序遍歷的結果,構造二叉樹(優化版)。* @param preorder 前序遍歷數組* @param inorder  中序遍歷數組* @return 構造出的二叉樹的根節點*/public TreeNode buildTree(int[] preorder, int[] inorder) {int n = preorder.length;// 步驟 1: 預處理,用哈希表存儲中序遍歷中值到索引的映射,以實現 O(1) 查找。Map<Integer, Integer> index = new HashMap<>(n);for (int i = 0; i < n; i++) {index.put(inorder[i], i);}// 調用輔助的 DFS 函數開始遞歸構建return dfs(preorder, 0, n, 0, n, index);}/*** 輔助 DFS 函數,通過索引范圍在原數組上構建子樹。* @param preorder 完整的前序遍歷數組* @param preL     當前子樹在前序數組中的左邊界(包含)* @param preR     當前子樹在前序數組中的右邊界(不包含)* @param inL      當前子樹在中序數組中的左邊界(包含)* @param inR      當前子樹在中序數組中的右邊界(不包含)* @param index    中序遍歷值到索引的映射哈希表* @return 構建好的子樹的根節點*/private TreeNode dfs(int[] preorder, int preL, int preR, int inL, int inR, Map<Integer, Integer> index) {// 遞歸的基線條件:如果范圍為空,則子樹為空。if (preL == preR) {return null;}// 根節點的值是前序遍歷范圍的第一個元素int rootVal = preorder[preL];// O(1) 時間從中序哈希表中獲取根節點的位置int inRootIndex = index.get(rootVal);// 計算左子樹的大小int leftSize = inRootIndex - inL;// 遞歸構建左子樹// 左子樹的前序范圍:[preL + 1, preL + 1 + leftSize)// 左子樹的中序范圍:[inL, inRootIndex)  (即 inL + leftSize)TreeNode left = dfs(preorder, preL + 1, preL + 1 + leftSize, inL, inRootIndex, index);// 遞歸構建右子樹// 右子樹的前序范圍:[preL + 1 + leftSize, preR)// 右子樹的中序范圍:[inRootIndex + 1, inR)TreeNode right = dfs(preorder, preL + 1 + leftSize, preR, inRootIndex + 1, inR, index);// 創建根節點,連接左右子樹,并返回return new TreeNode(rootVal, left, right);}
}

時空復雜度

時間復雜度:O(N)

  1. 哈希表預處理for 循環遍歷 inorder 數組一次,構建哈希表。時間復雜度為 O(N)
  2. 遞歸構建dfs 函數會對 preorder 數組中的每個元素處理一次(作為一次根節點)。
    • dfs 函數內部,所有的操作——哈希表查找、算術運算、創建新節點——都是 O(1) 的。
    • 由于每個節點只被訪問一次,總的遞歸構建時間為 N * O(1) = O(N)

綜合分析
總時間復雜度 = 預處理時間 + 遞歸構建時間 = O(N) + O(N) = O(N)

空間復雜度:O(N)

  1. 哈希表:算法創建了一個哈希表 index 來存儲中序遍歷的映射。在最壞情況下,所有元素都不同,哈希表需要存儲 N 個鍵值對。因此,哈希表占用的空間為 O(N)
  2. 遞歸調用棧:遞歸的深度取決于樹的高度 H
    • 在最好的情況下(一個完全二叉樹),H 約為 log N,遞歸棧空間為 O(log N)。
    • 在最壞的情況下(一個極度傾斜的鏈狀樹),H 等于 N,遞歸棧空間為 O(N)

綜合分析
算法所需的額外空間由哈希表和遞歸調用棧共同決定。總空間復雜度為 O(N) (哈希表) + O(H) (遞歸棧)。由于 H 的上界是 N,因此最壞情況下的總空間復雜度為 O(N) + O(N) = O(N)

參考靈神

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

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

相關文章

完美卸載 Ubuntu 雙系統:從規劃到實施的完整指南

&#x1f4d6; 前言 最近成功完成了一次 Ubuntu 雙系統的完整卸載&#xff0c;從最初的分區刪除到最終解決 GRUB 引導問題&#xff0c;整個過程雖然有些曲折&#xff0c;但最終完美解決。本文將詳細分享整個卸載過程&#xff0c;希望能幫助到有類似需求的朋友。 &#x1f3af…

深入理解oracle ADG和RAC

1. 引言 本節詳細介紹oracle ADG和RAC。當然這里講得的詳細是相對理論的深入&#xff0c;不涉及到實驗&#xff0c;比如ADG和RAC的搭建及調優等。 RAC (Real Application Clusters) 和 ADG (Active Data Guard)是Oracle 的兩大核心高可用和災備技術。它們是 Oracle 數據庫高可用…

網絡安全實踐:從環境搭建到漏洞復現

要求&#xff1a;1.搭建docker2.使用小皮面板搭建pikachu靶場3.使用BP的爆破模塊破解pikachu的登陸密碼步驟4.Kail的msf復現永恒之藍一.搭建docker1. Docker介紹Docker 是容器&#xff0c;可以部分完全封閉。封閉意味&#xff1a;一個物質&#xff08;放到容器&#xff09;&…

車載診斷架構 --- 診斷功能開發流程

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 做到欲望極簡,了解自己的真實欲望,不受外在潮流的影響,不盲從,不跟風。把自己的精力全部用在自己。一是去掉多余,凡事找規律,基礎是誠信;二是…

mysql數據庫知識

MySQL數據庫詳解MySQL是目前全球最流行的關系型數據庫管理系統之一&#xff0c;以其開源免費、高效穩定、易于擴展等特點&#xff0c;被廣泛應用于Web開發、企業級應用等場景。本文將從基礎概念、核心特性到實際應用&#xff0c;對MySQL進行全面解析。一、MySQL的基本概念1. 關…

基于springboot的美食文化和旅游推廣系統

博主介紹&#xff1a;java高級開發&#xff0c;從事互聯網行業多年&#xff0c;熟悉各種主流語言&#xff0c;精通java、python、php、爬蟲、web開發&#xff0c;已經做了多年的畢業設計程序開發&#xff0c;開發過上千套畢業設計程序&#xff0c;沒有什么華麗的語言&#xff0…

Rust賦能文心大模型4.5智能開發

文心大模型4.5版本概論 文心大模型4.5是百度推出的最新一代大規模預訓練語言模型,屬于文心大模型(ERNIE)系列。該模型在自然語言處理(NLP)、多模態理解與生成等領域表現出色,廣泛應用于智能搜索、內容創作、對話交互等場景。 核心能力 語言理解與生成 支持復雜語義理解…

前端抓包(不啟動前端項目就能進行后端調試)--whistle

1、安裝 1.1.安裝node.js 1.2.安裝whistle npm install -g whistle2.安裝瀏覽器插件【SwitchyOmega】在谷歌瀏覽器應用商店下載安裝即可配置proxy127.0.0.1:8989是w2 start的端口號啟用代理3.啟動服務&#xff08;每次抓包都得啟動&#xff09; w2 start點擊鏈接訪問網頁 http:…

kettle從入門到精通 第102課 ETL之kettle xxl-job調度kettle的兩種方式

之前我們一起學習過xxl-job調度carte&#xff0c;采用的xxl-job執行器方式&#xff0c;不了解的可以查看《kettle從入門到精通 第六十一課 ETL之kettle 任務調度器&#xff0c;輕松使用xxl-job調用kettle中的job和trans 》 今天我們一起來學習下使用xxl-job直接使用http調用…

純前端 JavaScript 實現數據導出到 CSV 格式

日常開發中&#xff0c;數據導出到文件通常有兩種方式&#xff1a; 在后端處理&#xff0c;以文件流或者資源路徑的方式返回&#xff1b;后端返回數據&#xff0c;前端按需處理后再觸發瀏覽器的下載事件&#xff0c;已保存到本地文件。 這里介紹后者的一種零依賴的實現方式。…

香港理工大學實驗室定時預約

香港理工大學實驗室定時預約 文章目錄香港理工大學實驗室定時預約簡介接單價格軟件界面網站預約界面代碼對爬蟲、逆向感興趣的同學可以查看文章&#xff0c;一對一小班教學(系統理論和實戰教程)、提供接單兼職渠道&#xff1a;https://blog.csdn.net/weixin_35770067/article/d…

Spring AI 項目實戰(十七):Spring Boot + AI + 通義千問星辰航空智能機票預訂系統(附完整源碼)

系列文章 序號文章名稱1Spring AI 項目實戰(一):Spring AI 核心模塊入門2Spring AI 項目實戰(二):Spring Boot + AI + DeepSeek 深度實戰(附完整源碼)3Spring AI 項目實戰(三):Spring Boot + AI + DeepSeek 打造智能客服系統(附完整源碼)4

STM32CubeMX+CLion 使用ARM_CMSIS_DSP

安裝 參考&#xff1a; 【CLion開發stm32】如何使用DSP庫 - 未知的奇跡 - 博客園 實際上這樣配置會出一點小問題&#xff0c;現對其修改 1. 項目根目錄下新建 DSP_LIB文件夾 將目錄STM32CubeMX\Repository\STM32Cube_FW_G4_V1.6.1\Drivers\CMSIS\DSP下的Include文件夾和So…

深入解析C#接口實現的兩種核心技術:派生繼承 vs 顯式實現

—— 如何優雅解決多接口沖突問題 &#x1f50d; 核心概念速覽 派生成員實現 類通過繼承基類方法隱式滿足接口實現需求 interface IIfc1 { void PrintOut(string s); }class MyBaseClass { // 基類實現方法 public void PrintOut(string s) > Console.WriteLine($"Cal…

鴻蒙項目構建配置

鴻蒙項目構建配置 參考文檔 深入鴻蒙開發之后&#xff0c;一般會遇到以下幾個問題。 每次編譯的時候需要手動配置不同的 versionCode 和 versionName&#xff1b;在使用 git 管理代碼的時候&#xff0c;不同的人或者不在同一臺電腦上&#xff0c;dev eco 這個編譯器需要經常…

os.machine()詳解

核心功能返回硬件架構 返回字符串表示系統的硬件架構&#xff0c;常見值包括&#xff1a; x86_64&#xff1a;64 位 x86 架構&#xff08;Intel/AMD&#xff09;armv7l&#xff1a;32 位 ARM 架構&#xff08;如樹莓派 3B&#xff09;aarch64&#xff1a;64 位 ARM 架構&#x…

linux-shell腳本

linux-shell腳本一、什么是shell腳本&#xff1f;二、為什么要學習shell腳本&#xff1f;三、腳本執行的方式3.1 bash test.sh3.2 ./test.sh3.3 source test.sh3.4 . test.sh四、變量的使用4.1 變量定義與使用4.2 避免變量混淆4.3 位置變量for循環和位置變量的結合案例4.4 read…

【嵌入式】51單片機學習筆記-Keil5軟件安裝教程

00. 目錄 文章目錄00. 目錄01. Keil C51概述02. Keil C51下載03. Keil C51安裝04. Keil C51注冊05. 附錄01. Keil C51概述 Keil C51 是德國Keil公司&#xff08;現被ARM收購&#xff09;開發的嵌入式開發工具&#xff0c;專注于8051單片機的C語言和匯編開發。它是μVision IDE…

ai之 ubuntu本地安裝mineru2.1.0

MinerU 目錄 一、更新內容概述寫在前面的話:總體來看,2.0版本升級為全新的 VLM 解析模式,更優于以前的基礎解析方式。二、MinerU 安裝部署下面使用源碼來進行環境安裝。注意:當前狀態說明推薦解決方案如果是下載插件慢可以 指定阿里源三、MinerU 使用1. 在線體驗2. 命令行使…

華為昇騰NPU與NVIDIA CUDA生態兼容層開發實錄:手寫算子自動轉換工具鏈(AST級代碼遷移方案)

點擊 “AladdinEdu&#xff0c;同學們用得起的【H卡】算力平臺”&#xff0c;H卡級別算力&#xff0c;按量計費&#xff0c;靈活彈性&#xff0c;頂級配置&#xff0c;學生專屬優惠。 當國產AI芯片崛起遭遇生態壁壘&#xff0c;如何實現CUDA算子到昇騰平臺的無損遷移成為關鍵挑…