通過先序遍歷和中序遍歷後的序列還原二叉樹

来源:http://www.cnblogs.com/caijh/archive/2017/06/02/6935645.html
-Advertisement-
Play Games

當我們有一個 先序遍歷序列:1,3,7,9,5,11 中序遍歷序列:9,7,3,1,5,11 我們可以很輕鬆的用筆寫出對應的二叉樹。但是用代碼又該如何實現? 下麵我們來簡單談談基本思想。 首先,先序遍歷的順序是根據 根-左孩子-右孩子 的順序遍歷的,那麼我們可以率先確認的是先序遍歷序列的第一個數就是 ...


當我們有一個

先序遍歷序列:1,3,7,9,5,11

中序遍歷序列:9,7,3,1,5,11

我們可以很輕鬆的用筆寫出對應的二叉樹。但是用代碼又該如何實現?

下麵我們來簡單談談基本思想。

首先,先序遍歷的順序是根據 根-左孩子-右孩子 的順序遍歷的,那麼我們可以率先確認的是先序遍歷序列的第一個數就是根節點,然後中序遍歷是根據 左孩子-根-右孩子 的順序遍歷的。我們通過先序遍歷確認了根節點,那麼我們只需要在中序遍歷中找到根節點的位置,然後就可以很好地區分出,那些屬於左子樹的節點,那些是屬於右子樹的節點了。如下圖:

我們確定數字1為根節點,然後根據中序遍歷的遍歷順序確定,中序遍歷序列中數字1的左邊全部為左子樹節點,右邊全部為右子樹。通過左子樹節點的個數,得出先序遍歷序列中從根節點往後的連續3個數是屬於左子樹的,剩下的為右子樹。這樣再在左右子樹的序列中重覆以上步驟,最終找到沒有子節點為止。

實現代碼如下:

  1 package com.tree.traverse;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 
  6 /**
  7  * @author Caijh
  8  *
  9  * 2017年6月2日 下午7:21:10
 10  */
 11 
 12 public class BuildTreePreOrderInOrder {
 13 
 14     /** 
 15      *              1 
 16      *             / \
 17      *            3   5 
 18      *           /     \
 19      *          7       11
 20      *       /  
 21      *      9       
 22      */  
 23     public static int treeNode = 0;//記錄先序遍歷節點的個數
 24     private List<Node> nodeList = new ArrayList<>();//層次遍歷節點的隊列
 25     public static void main(String[] args) {
 26         BuildTreePreOrderInOrder build = new BuildTreePreOrderInOrder();
 27         int[] preOrder = { 1, 3, 7, 9, 5, 11};
 28         int[] inOrder = { 9, 7, 3, 1, 5, 11};
 29         
 30         treeNode = preOrder.length;//初始化二叉樹的節點數
 31         Node root = build.buildTreePreOrderInOrder(preOrder, 0, preOrder.length - 1, inOrder, 0, preOrder.length - 1);
 32         System.out.print("先序遍歷:");
 33         build.preOrder(root);
 34         System.out.print("\n中序遍歷:");
 35         build.inOrder(root);
 36         System.out.print("\n原二叉樹:\n");
 37         build.prototypeTree(root);
 38     }
 39 
 40     /**
 41      * 分治法
 42      * 通過先序遍歷結果和中序遍歷結果還原二叉樹
 43      * @param preOrder    先序遍歷結果序列
 44      * @param preOrderBegin     先序遍歷起始位置下標
 45      * @param preOrderEnd    先序遍歷末尾位置下標
 46      * @param inOrder    中序遍歷結果序列
 47      * @param inOrderBegin    中序遍歷起始位置下標
 48      * @param inOrderEnd     中序遍歷末尾位置下標
 49      * @return
 50      */
 51     public Node buildTreePreOrderInOrder(int[] preOrder, int preOrderBegin, int preOrderEnd, int[] inOrder, int inOrderBegin, int inOrderEnd) {
 52         if (preOrderBegin > preOrderEnd || inOrderBegin > inOrderEnd) {
 53             return null;
 54         }
 55         int rootData = preOrder[preOrderBegin];//先序遍歷的第一個字元為當前序列根節點
 56         Node head = new Node(rootData);
 57         int divider = findIndexInArray(inOrder, rootData, inOrderBegin, inOrderEnd);//找打中序遍歷結果集中根節點的位置
 58         int offSet = divider - inOrderBegin - 1;//計算左子樹共有幾個節點,節點數減一,為數組偏移量
 59         Node left = buildTreePreOrderInOrder(preOrder, preOrderBegin + 1, preOrderBegin + 1 + offSet, inOrder, inOrderBegin,inOrderBegin + offSet);
 60         Node right = buildTreePreOrderInOrder(preOrder, preOrderBegin + offSet + 2, preOrderEnd, inOrder, divider + 1, inOrderEnd);
 61         head.left = left;
 62         head.right = right;
 63         return head;
 64     }
 65     /**
 66      * 通過先序遍歷找到的rootData根節點,在中序遍歷結果中區分出:中左子樹和右子樹
 67      * @param inOrder    中序遍歷的結果數組
 68      * @param rootData    根節點位置
 69      * @param begin    中序遍歷結果數組起始位置下標
 70      * @param end    中序遍歷結果數組末尾位置下標
 71      * @return return中序遍歷結果數組中根節點的位置
 72      */
 73     public int findIndexInArray(int[] inOrder, int rootData, int begin, int end) {
 74         for (int i = begin; i <= end; i++) {
 75             if (inOrder[i] == rootData)
 76                 return i;
 77         }
 78         return -1;
 79     }
 80     /**
 81      * 二叉樹先序遍歷結果
 82      * @param n
 83      */
 84     public void preOrder(Node n) {
 85         if (n != null) {
 86             System.out.print(n.val + ",");
 87             preOrder(n.left);
 88             preOrder(n.right);
 89         }
 90     }
 91     /**
 92      * 二叉樹中序遍歷結果
 93      * @param n
 94      */
 95     public void inOrder(Node n) {
 96         if (n != null) {
 97             inOrder(n.left);
 98             System.out.print(n.val + ",");
 99             inOrder(n.right);
100         }
101     }
102     /**
103      * 還原後的二叉樹
104      * 二叉數層次遍歷
105      * 基本思想:
106      *     1.因為推導出來的二叉樹是保存在Node類對象的子對象裡面的,(類似於c語言的結構體)如果通過遞歸實現層次遍歷的話,不容易實現
107      *     2.這裡採用List隊列逐層保存Node對象節點的方式實現對二叉樹的層次遍歷輸出
108      *     3.如果父節點的位置為i,那麼子節點的位置為,2i 和 2i+1;依據這個規律逐層遍歷,通過保存的父節點,找到子節點。並保存,不斷向下遍歷保存。
109      * @param tree
110      */
111     public void prototypeTree(Node tree){
112         //用list存儲層次遍歷的節點
113         if(tree !=null){
114             if(tree!=null)
115                 nodeList.add(tree);
116             nodeList.add(tree.left);
117             nodeList.add(tree.right);
118             int count=3;
119             //從第三層開始
120             for(int i=3;count<treeNode;i++){
121                 //第i層第一個子節點的父節點的位置下標
122                 int index = (int) Math.pow(2, i-1-1)-1;
123                 /**
124                  * 二叉樹的每一層節點數遍歷
125                  * 因為第i層的最大節點數為2的i-1次方個,
126                  */
127                 for(int j=1;j<=Math.pow(2, i-1);){
128                     //計算有效的節點的個數,和遍歷序列的總數做比較,作為判斷迴圈結束的標誌
129                     if(nodeList.get(index).left!=null)
130                         count++;
131                     if(nodeList.get(index).right!=null)
132                         count++;
133                     nodeList.add(nodeList.get(index).left);
134                     nodeList.add(nodeList.get(index).right);
135                     index++;
136                     if(count>=treeNode)//當所有有效節點都遍歷到了就結束遍歷
137                         break;
138                     j+=2;//每次存儲兩個子節點,所以每次加2
139                 }
140             }
141             int flag=0,floor=1;
142             for(Node node:nodeList){
143                 if(node!=null)
144                     System.out.print(node.val+" ");
145                 else
146                     System.out.print("# ");//#號表示空節點
147                 flag++;
148                 /**
149                  * 逐層遍歷輸出二叉樹
150                  * 
151                  */
152                 if(flag>=Math.pow(2, floor-1)){
153                     flag=0;
154                     floor++;
155                     System.out.println();
156                 }
157             }
158         }
159     }
160     /**
161      * 內部類
162      * 1.每個Node類對象為一個節點,
163      * 2.每個節點包含根節點,左子節點和右子節點
164      */
165     class Node {
166         Node left;
167         Node right;
168         int val;
169         public Node(int val) {
170             this.val = val;
171         }
172     }
173 }

 

運行結果:

最後逐層輸出二叉樹的基本思想:
* 1.因為推導出來的二叉樹是保存在Node類對象的子對象裡面的,(類似於c語言的結構體)如果通過遞歸實現層次遍歷的話,不容易實現
* 2.這裡採用List隊列逐層保存Node對象節點的方式實現對二叉樹的層次遍歷輸出
* 3.如果父節點的位置為i,那麼子節點的位置為,2i 和 2i+1;依據這個規律逐層遍歷,通過保存的父節點,找到子節點。並保存,不斷向下遍歷保存。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本章和大家分享的內容是使用Signal R框架創建個簡易的群聊功能,主要講解如何在.Net的MVC中使用這個框架,由於這個項目有官方文檔(當然全英文),後面也不打算寫分享篇了,主要目的是讓朋友們在需要使用Web實時通信的時候有更多一種解決方案,畢竟這是微軟主推的一種解決方案之一。 SignalR網上 ...
  • NuGet包地址: https://www.nuget.org/packages/OYMLCN.WeChat.Core 由於來的OYMLCN.WeChat存在深度封裝,並沒有做完整的測試,對於使用不友好,現已重新構建SDK的接收消息被動回覆模塊。 現已做到最大程度的簡易化及模塊化,每個模塊都已完成單 ...
  • 回到目錄 在MVC,EF,LINQ環境里,我們經常會用到DataModel(DO)和ViewModel(VO),可能對於它們的屬性校驗我們會採用特性的方式,當然這很直觀,就連微軟的DEMO也是如些,一般是這樣的代碼 而這種設計方式給我們以後的維護帶來很多問題,具體大叔總結一下: 綜上所述,Fluen ...
  • Java基礎十二--多態是成員的特點 一、特點 1,成員變數。 編譯和運行都參考等號的左邊。 覆蓋只發生在函數上,和變數沒關係。 Fu f = new Zi();System.out.println(f.num);//是父類,答案是3 2,成員函數(非靜態)。 編譯看左邊,運行看右邊。 因為成員函數 ...
  • 頭文件algorithm中的常用函數 一、非修改性序列操作(12個) 迴圈 對序列中的每個元素執行某操作 for_each() 查找 在序列中找出某個值的第一次出現的位置 find() 在序列中找出符合某謂詞的第一個元素 find_if() 在序列中找出一子序列的最後一次出現的位置 find_end ...
  • 慕課網Hibernate初探之一對多映射實驗及總結 一、本課核心 * 1、如何在MyEclipse中使用Hibernate * 2、如何實現Hibernate中一對多的映射 * 3、如何創建Session對象 * 4、Hibernate如何使用增刪改查 1、如何在MyEclipse中使用Hibern ...
  • 學習一門技術,不止要會,還要善用,例子就是帶你快速入門的最佳利器。本文就是要用例子,不,大量的例子來帶你走進PowerShell應用世界。 本文主要介紹一些PowerShell入門的基礎知識,對技術小白來說可以快速入門,對技術老鳥來說可以複習鞏固,廢話不多說,直接進入正題。 PowerShell,相 ...
  • 一 概述 1.什麼是進程? 進程是一個相對獨立的執行單位。 2.什麼是線程? 進程的一部分,進程中實際的任務執行者,必須依附於進程。線程對進程的依賴主要體現在: 線程不能脫離進程開啟,必須在進程開啟的前提下開啟。 線程有時必須從進程中獲取數據。 3.線程與進程的區別? 線程與進程是兩個相對的概念,一 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...