算法-根據前序+中序遍歷打印樹的右視圖

題目

請根據二叉樹的前序遍歷,中序遍歷恢復二叉樹,并打印出二叉樹的右視圖

數據范圍:?0≤n≤100000≤n≤10000
要求: 空間復雜度?O(n)O(n),時間復雜度?O(n)O(n)

如輸入[1,2,4,5,3],[4,2,5,1,3]時,通過前序遍歷的結果[1,2,4,5,3]和中序遍歷的結果[4,2,5,1,3]可重建出以下二叉樹:

C++代碼實現:

TreeNode* buildTree(vector<int> & xianxu, int l1, int r1, vector<int> &zhongxu, int l2, int r2){if (l1 > r1 || l2 > r2) return NULL;TreeNode* root = new TreeNode(xianxu[l1]);int rootIndex = 0;for (int i = l2; i <= r2; ++i){if (zhongxu[i] == xianxu[l1]){rootIndex = i;break;}}int leftsize = rootIndex - l2;int rightsize = r2 - rootIndex;root->left = buildTree(xianxu, l1+1, l1+leftsize, zhongxu, l2, l2+leftsize-1);root->right = buildTree(xianxu, l1+leftsize+1, r1, zhongxu, rootIndex+1, r2);return root;}vector<int> rightSideView(TreeNode *root){unordered_map<int, int> mp;int max_depth = -1;stack<TreeNode*> nodes;stack<int> depth;nodes.push(root);depth.push(0);while (!nodes.empty()){TreeNode* node = nodes.top();nodes.pop();int depth1 = depth.top();depth.pop();if (node != NULL)   {max_depth = max(depth1, max_depth);if (mp.find(depth1) == mp.end()){mp[depth1] = node->val;}nodes.push(node->left);nodes.push(node->right);depth.push(depth1+1);depth.push(depth1+1);} }vector<int> res;for (int i = 0; i <= max_depth; ++i){res.push_back(mp[i]);}return res;}vector<int> solve(vector<int>& preOrder, vector<int>& inOrder) {vector<int> res;if (preOrder.size() == 0) return res;TreeNode* root = buildTree(preOrder, 0, preOrder.size()-1, inOrder, 0, inOrder.size()-1);return rightSideView(root);}

右視圖是啥?

右視圖就是「站在二叉樹右邊看過去,能看到的每個層的最右邊節點」。比如下面這個樹:

    1        (第0層,看到1)/   \2     3     (第1層,看到3)
/     / \
4     5   6   (第2層,看到6)

右視圖結果就是?[1,3,6]—— 核心是要拿到「每一層的第一個遇到的最右節點」。

為什么用棧?棧在這里的作用

棧是 “先進后出” 的容器,適合深度優先遍歷(先往深了走,走到頭再回頭)。這里用了?兩個棧

  • nodes?棧:存要遍歷的二叉樹節點;
  • depth?棧:存對應節點的 “深度”(根節點深度是 0,子節點比父節點深 1)。
    兩個棧是 “同步操作” 的 —— 彈出一個節點,就對應彈出它的深度;壓入節點時,也對應壓入它的深度。

逐行拆解代碼邏輯(結合例子看更清楚)

咱們就用上面的樹?[1,2,3,4,5,6]?當例子,一步一步看棧和數據的變化。

初始狀態

一開始,把根節點?1?和它的深度?0?分別壓入兩個棧:

  • nodes?棧:[1](棧頂是 1)
  • depth?棧:[0](棧頂是 0)
  • mp(存 “深度→最右節點值”):空
  • max_depth(記錄最大深度):-1
進入循環:while (!nodes.empty ())(棧不空就繼續)

循環的核心邏輯是:彈出棧頂節點→判斷是不是有效節點→處理有效節點→壓入它的子節點

第一步:彈出節點和對應深度
TreeNode* node = nodes.top();  // 取nodes棧頂節點(一開始是1)
nodes.pop();                   // 把1從nodes棧彈出,nodes現在空了
int depth1 = depth.top();      // 取depth棧頂深度(0)
depth.pop();                   // 把0從depth棧彈出,depth現在空了

此時:node=1depth1=0

第二步:判斷節點是否有效(非 NULL)
if (node != NULL)  // 1不是NULL,進入處理
{// 1. 更新最大深度:當前深度0比初始的-1大,所以max_depth=0max_depth = max(depth1, max_depth);  // max(0,-1)=0// 2. 記錄當前深度的最右節點(關鍵!)if (mp.find(depth1) == mp.end())  // 查mp里有沒有深度0的記錄?一開始沒有{mp[depth1] = node->val;  // 把深度0和值1存進去,mp現在是 {0:1}}// 3. 壓入當前節點的左、右子節點(重點!棧是先進后出,所以先壓左,再壓右)nodes.push(node->left);   // 壓入1的左子節點2 → nodes棧:[2]nodes.push(node->right);  // 再壓入1的右子節點3 → nodes棧:[2,3](棧頂是3)// 4. 同步壓入子節點的深度(父節點深度+1=0+1=1)depth.push(depth1+1);  // 壓入2的深度1 → depth棧:[1]depth.push(depth1+1);  // 再壓入3的深度1 → depth棧:[1,1](棧頂是1)
}

這一步結束后

  • nodes?棧:[2,3](棧頂是 3)
  • depth?棧:[1,1](棧頂是 1)
  • mp{0:1}
  • max_depth:0
第三步:繼續循環(nodes 棧不空,處理棧頂的 3)

重復第一步:彈出節點和深度

  • node = nodes.top()?→ 3;nodes.pop()?→ nodes 變成 [2]
  • depth1 = depth.top()?→ 1;depth.pop()?→ depth 變成 [1]

判斷 3 非 NULL,進入處理:

  1. 更新 max_depth:max (1,0)=1 → max_depth=1;
  2. 查 mp 有沒有深度 1 的記錄?沒有 → mp [1]=3 → mp 現在 {0:1, 1:3};
  3. 壓入 3 的左、右子節點:
    • 3 的左是 5,右是 6 → 先壓左(5),再壓右(6)→ nodes 棧變成 [2,5,6](棧頂是 6);
  4. 同步壓入深度 1+1=2 → depth 棧變成 [1,2,2](棧頂是 2)。

這一步結束后:

  • nodes?棧:[2,5,6]
  • depth?棧:[1,2,2]
  • mp{0:1, 1:3}
  • max_depth:1
第四步:繼續循環(處理棧頂的 6)

彈出 6 和深度 2:

  • node=6depth1=2(非 NULL);
  1. 更新 max_depth:max (2,1)=2 → max_depth=2;
  2. 查 mp 有沒有深度 2 的記錄?沒有 → mp [2]=6 → mp 現在 {0:1,1:3,2:6};
  3. 壓入 6 的左、右子節點(都是 NULL)→ nodes 棧變成 [2,5,NULL,NULL];
  4. 同步壓入深度 3 → depth 棧變成 [1,2,3,3]。

這一步后,mp?已經存好了所有層的最右節點,后續再處理 NULL 節點和剩下的 2、5,都不會修改 mp 了(因為 mp 里對應深度已有值)。

后續循環:處理 NULL 和剩余節點

比如處理 6 的左子節點(NULL):彈出后判斷是 NULL,直接跳過,不做任何處理;
處理 5 時,深度是 2,但 mp 里已有深度 2 的記錄(6),所以不會覆蓋;
處理 2 時,深度是 1,mp 里已有深度 1 的記錄(3),也不會覆蓋。

直到棧空,循環結束。

最后:生成結果
vector<int> res;
for (int i = 0; i <= max_depth; ++i)  // 從深度0到2,依次取mp的值
{res.push_back(mp[i]);  // 0→1,1→3,2→6 → res=[1,3,6]
}
return res;

關鍵:為什么先壓左子節點,再壓右子節點?

這是保證拿到「最右節點」的核心
因為棧是 “先進后出”:先壓左,再壓右 → 下一次彈出時,會先彈右子節點
比如節點 1 的子節點:先壓 2,再壓 3 → 下次先彈 3(右節點),這樣 3 就會被優先處理,成為深度 1 的最右節點;
節點 3 的子節點:先壓 5,再壓 6 → 下次先彈 6(右節點),成為深度 2 的最右節點。

如果反過來先壓右再壓左,就會先處理左節點,拿到的就是「左視圖」了

總結:這段代碼的邏輯一句話說

用兩個棧同步存節點和深度,通過 “先壓左、再壓右” 保證每次先處理層的右節點,用哈希表記錄每一層第一個遇到的右節點(就是最右節點),最后按深度順序收集結果,就是右視圖。

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

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

相關文章

Kafka面試精講 Day 7:消息序列化與壓縮策略

【Kafka面試精講 Day 7】消息序列化與壓縮策略 在Kafka的高性能消息系統中&#xff0c;消息序列化與壓縮是影響吞吐量、延遲和網絡開銷的核心環節。作為“Kafka面試精講”系列的第7天&#xff0c;本文聚焦于這一關鍵主題&#xff0c;深入剖析其原理、實現方式、配置策略及常見…

Xterminal軟件下載_Xterminal ssh遠程鏈接工具下載__Xterminal安裝包 網盤下載_Xterminal ssh遠程鏈接工具安裝包

Xterminal 作為一款國產 SSH 工具&#xff0c;專為開發人員量身打造。它支持 SSH 和 Telnet 協議連接遠程服務器與虛擬機&#xff0c;無論是進行代碼部署&#xff0c;還是服務器運維&#xff0c;都能輕松勝任。軟件界面采用極簡設計&#xff0c;黑色背景搭配白色文字&#xff0…

Lua > 洛谷

Lua > 洛谷P1000 超級瑪麗游戲P1001 AB ProblemP1008 [NOIP 1998 普及組] 三連擊P1035 [NOIP 2002 普及組] 級數求和P1046 [NOIP 2005 普及組] 陶陶摘蘋果P1047 [NOIP 2005 普及組] 校門外的樹P1085 [NOIP 2004 普及組] 不高興的津津P1089 [NOIP 2004 提高組] 津津的儲蓄計劃…

小企業環境-火山方舟和扣子

背景說明 并不是說應該怎么辦&#xff0c;而是基本配置有這些可以進行使用&#xff0c;具體不同企業使用的時候肯定要個性化配置。 使用了火山方舟和扣子 火山方舟 應用實驗室列表 簡單使用了提示詞的功能&#xff0c;后端服務ARK_API_KEY 應用ID 來對應請求發送http請求…

QT-事件

Qt事件 除了信號和槽通信機制外&#xff0c;Qt中還提供了事件處理機制實現與用戶的交互和對象間的通信。Qt捕獲底層操作系統消息&#xff0c;進行封裝之后轉換為Qt事件&#xff0c;事件處理后才發出信號。 一、事件概述Qt中事件是程序內部或外部發生的動作。比如程序外部&#…

HI3519DRFCV500/HI3519DV500海思核心板IPC算力2.5T圖像ISP超高清智能視覺應用提供SDK軟件開發包

Hi3519DV500是一顆面向視覺行業推出的超高清智能 SoC。最高支持四路sensor輸入&#xff0c;支持最高4K30fps的ISP圖像處理能力&#xff0c;支持 2F WDR、多級降噪、六軸防抖、全景拼接、多光 譜融合等多種傳統圖像增強和處理算法&#xff0c;支持通過AI算法對輸入圖像進行實時降…

go 初始化組件最佳實踐

Go 語言初始化最佳實踐 在 Go 語言中, 有一個 init() 函數可以對程序進行包級別的初始化, 但 init() 函數有諸多不便, 例如: 無法返回錯誤, 進行耗時初始化時, 會增加程序啟動時間。因此 init() 函數并不適用于所有初始化。 1.初始化方式 在程序進行初始化時&#xff0c;我們應…

域名暫停解析是怎么回事

域名注冊和使用是需要付費的&#xff0c;如果沒有及時續費&#xff0c;域名注冊商就會暫停該域名的解析服務。相關數據顯示&#xff0c;大約有 30% 的域名暫停解析情況是由于欠費引起的。比如&#xff0c;有個小公司的網站域名到期了&#xff0c;負責續費的員工忘記操作&#x…

前端開發的“三劍客”—— ??HTML、CSS、JavaScript??

前端開發的“三劍客”—— ??HTML、CSS、JavaScript??&#xff0c;是構建所有網頁和Web應用的基石。它們分工明確又緊密協作&#xff0c;共同實現了網頁的“內容結構”“視覺表現”和“交互行為”。以下是三者的詳細解析及協作邏輯&#xff1a;??1. HTML&#xff1a;網頁…

TDengine TIMEDIFF() 函數用戶使用手冊

TDengine TIMEDIFF() 函數詳細使用手冊 目錄 功能概述函數語法參數說明返回值說明版本變更說明技術特性使用場景及示例時間單位處理數據類型兼容性注意事項常見問題最佳實踐 功能概述 TIMEDIFF() 函數用于計算兩個時間戳的差值&#xff0c;返回 expr1 - expr2 的結果。結果…

數據結構:棧和隊列(上)

匯總代碼見&#xff1a;登錄 - Gitee.com 上一篇文章&#xff1a;數據結構&#xff1a;雙向鏈表-CSDN博客 與本文相關的結構體傳參&#xff1a;自定義類型&#xff1a;結構體-CSDN博客 1.棧 1.1概念和結構 棧&#xff1a;一種特殊的線性表&#xff0c;其只允許在固定的一端…

文檔抽取技術:提取非結構化文檔中的關鍵信息,提升檔案管理、金融保險和法律合規領域的效率與準確性

在信息爆炸的時代&#xff0c;各種機構、企業等都面臨著海量非結構化文檔數據的挑戰。報告、合同、票據、檔案記錄、法律文書等文檔中蘊藏著巨大的數據&#xff0c;但傳統依靠人工閱讀、理解和錄入的方式效率低下、成本高昂且容易出錯。文檔抽取技術作為人工智能和自然語言處理…

雷柏VT1 MAX評測:原生中小手形電競鼠標 但既不僅限于中小手形 也不僅限于電競

一、前言&#xff1a;真正針對中小手形設計的電競鼠標 雷柏第二代VT系列電競鼠標我們已經體驗過很多款了&#xff0c;基本都是針對大中手形設計的外形模具&#xff0c;只有VT3s系列是VT3系列的縮小版&#xff0c;更適合中小手形使用&#xff0c;但也只是對中大手形模具重新優化…

新客戶 | TDengine 時序數據庫賦能開源鴻蒙物聯展區實時監控與展示

在工業物聯網快速發展的當下&#xff0c;企業普遍面臨著兩大挑戰&#xff1a;一是設備種類繁多、接入標準不一&#xff0c;導致系統建設容易陷入“數據孤島”&#xff1b;二是實時監控和多場景聯動的需求越來越強烈&#xff0c;但傳統數據庫在高頻寫入與多維分析上難以兼顧&…

深入剖析 ConcurrentHashMap:Java 并發編程的基石

目錄 【1】Java 7 中 ConcurrentHashMap 的實現原理 1.分段鎖&#xff08;Segment&#xff09; 2. 數據結構 3. 操作流程 【2】Java 8 中 ConcurrentHashMap 的改進 1.紅黑樹的引入 2.CAS 操作 3.數據結構的變化 【3】ConcurrentHashMap 的常用方法及使用示例 1.put(…

【會員專享數據】2020-2022年我國鄉鎮的逐日地表氣壓數據(Shp/Excel格式)

之前我們分享過2020—2022年中國0.01分辨率逐日地表氣壓柵格數據&#xff08;可查看之前的文章獲悉詳情&#xff09;&#xff01;該數據是研究者張凌, 胡英屹等發布在國家冰川凍土沙漠科學數據中心平臺上的高分辨地表氣壓數據。很多小伙伴拿到數據后反饋柵格數據不太方便使用&a…

第二階段WinForm-12:UI控件庫

1_驗證碼與條形碼 1.1_條碼基礎知識 條碼&#xff1a;條碼是由一組按一定編碼規則排列的條、空符號組成&#xff0c;用以表示一定的字符、數字及符號組成的信息 1.2_一維碼 &#xff08;1&#xff09;Code 128 Code 128 是一種密度很高的字母數字代碼系統&#xff0c;可對其…

別再誤會了!Redis 6.0 的多線程,和你想象的完全不一樣

技術解析核心誤區&#xff1a;Redis 6.0是完全多線程的嗎&#xff1f;No. Redis 6.0引入的多線程&#xff0c;只用于網絡I/O的讀寫和數據的解析。而核心的命令執行&#xff08;比如 GET, SET, HGETALL 等&#xff09;依然是單線程的。Redis的架構演進&#xff0c;就像是把一個復…

23種設計模式——抽象工廠模式(Abstract Factory Pattern)詳解

?作者簡介&#xff1a;大家好&#xff0c;我是 Meteors., 向往著更加簡潔高效的代碼寫法與編程方式&#xff0c;持續分享Java技術內容。 &#x1f34e;個人主頁&#xff1a;Meteors.的博客 &#x1f49e;當前專欄&#xff1a;設計模式 ?特色專欄&#xff1a;知識分享 &#x…

本地部署開源數據生成器項目實戰指南

本地部署開源數據生成器項目實戰指南 前言 在當今大數據和人工智能時代&#xff0c;高質量數據集對于模型訓練和算法開發至關重要。然而&#xff0c;獲取真實且合規的數據集往往面臨隱私、成本和法律等多重挑戰。合成數據生成技術為此提供了優雅的解決方案&#xff0c;它能夠…