在之前的文章中我們有講過樹的相關知識,例如,樹的概念、深度優先遍歷和廣度優先遍歷。這篇文章講述了一個特殊的樹——二叉樹。
什么是二叉樹
二叉樹是每個節點最多只能有兩個子節點的樹,如下圖所示:
一個二叉樹具有以下幾個特質:
要計算在每層有多少個點,可以依據公式2^(i-1)個(i是樹的第幾層);
如果這顆二叉樹的深度為k,那二叉樹最多有2^k-1個節點;
在一個非空的二叉樹中,若使用n0表示葉子節點的個數,n2是度為2的非葉子節點的個數,那么兩者滿足關系n0 = n2 + 1。
滿二叉樹
如果在一個二叉樹中,除了葉子節點,其余的節點的每個度都是2,則說明該二叉樹是一個滿二叉樹,
如下圖所示:
滿二叉樹除了滿足普通二叉樹特質,還具有如下幾個特質:
每個層節點公式2^(n-1)(n為層數);
深度為k的滿二叉樹一定存在2^k-1個節點,葉子節點的個數為2^(k-1);
具有n個節點的滿二叉樹的深度為log_2^(n+1)。
完全二叉樹
如果一個二叉樹去掉最后一次層是滿二叉樹,且最后一次的節點是依次從左到右分布的,則這個二叉樹是一個完全二叉樹,
如下圖所示:
二叉樹的存儲
存儲二叉樹的常見方式分為兩種,一種是使用數組存儲,另一種使用鏈表存儲。
數組存儲
使用數組存儲二叉樹,如果遇到完全二叉樹,存儲順序從上到下,從左到右,如下圖所示:
如果是一個非完全二叉樹,如下圖所示:
需要先將其轉換為完全二叉樹,然后在進行存儲,如下圖所示:
可以很明顯的看到存儲空間的浪費。
鏈表存儲
使用鏈表存儲通常將二叉樹中的分為3個部分,如下圖:
這三個部分依次是左子樹的引用,該節點包含的數據,右子樹的引用,存儲方式如下圖所示:
與二叉樹相關的算法
以下算法中遍歷用到的樹如下:
// tree.js const bt = { val: 'A', left: { val: 'B', left: { val: 'D', left: null, right: null }, right: { val: 'E', left: null, right: null }, }, right: { val: 'C', left: { val: 'F', left: { val: 'H', left: null, right: null }, right: { val: 'I', left: null, right: null }, }, right: { val: 'G', left: null, right: null }, }, } module.exports = bt
深度優先遍歷
二叉樹的深度優先遍歷與樹的深度優先遍歷思路一致,思路如下:
訪問根節點;
訪問根節點的left
訪問根節點的right
重復執行第二三步
實現代碼如下:
const bt = { val: 'A', left: { val: 'B', left: { val: 'D', left: null, right: null }, right: { val: 'E', left: null, right: null }, }, right: { val: 'C', left: { val: 'F', left: { val: 'H', left: null, right: null }, right: { val: 'I', left: null, right: null }, }, right: { val: 'G', left: null, right: null }, }, } function dfs(root) { if (!root) return console.log(root.val) root.left && dfs(root.left) root.right && dfs(root.right) } dfs(bt) /** 結果 A B D E C F H I G */
廣度優先遍歷
實現思路如下:
創建隊列,把根節點入隊
把對頭出隊并訪問
把隊頭的left和right依次入隊
重復執行2、3步,直到隊列為空
實現代碼如下:
function bfs(root) { if (!root) return const queue = [root] while (queue.length) { const node = queue.shift() console.log(node.val) node.left && queue.push(node.left) node.right && queue.push(node.right) } } bfs(bt) /** 結果 A B C D E F G H I */
先序遍歷
二叉樹的先序遍歷實現思想如下:
訪問根節點;
對當前節點的左子樹進行先序遍歷;
對當前節點的右子樹進行先序遍歷;
如下圖所示:
遞歸方式實現如下:
const bt = require('./tree') function preorder(root) { if (!root) return console.log(root.val) preorder(root.left) preorder(root.right) } preorder(bt) /** 結果 A B D E C F H I G */
迭代方式實現如下:
// 非遞歸版 function preorder(root) { if (!root) return // 定義一個棧,用于存儲數據 const stack = [root] while (stack.length) { const node = stack.pop() console.log(node.val) /* 由于棧存在先入后出的特性,所以需要先入右子樹才能保證先出左子樹 */ node.right && stack.push(node.right) node.left && stack.push(node.left) } } preorder(bt) /** 結果 A B D E C F H I G */
中序遍歷
二叉樹的中序遍歷實現思想如下:
對當前節點的左子樹進行中序遍歷;
訪問根節點;
對當前節點的右子樹進行中序遍歷;
如下圖所示:
遞歸方式實現如下:
const bt = require('./tree') // 遞歸版 function inorder(root) { if (!root) return inorder(root.left) console.log(root.val) inorder(root.right) } inorder(bt) /** 結果 D B E A H F I C G */
迭代方式實現如下:
// 非遞歸版 function inorder(root) { if (!root) return const stack = [] // 定義一個指針 let p = root // 如果棧中有數據或者p不是null,則繼續遍歷 while (stack.length || p) { // 如果p存在則一致將p入棧并移動指針 while (p) { // 將 p 入棧,并以移動指針 stack.push(p) p = p.left } const node = stack.pop() console.log(node.val) p = node.right } } inorder(bt) /** 結果 D B E A H F I C G */
后序遍歷
二叉樹的后序遍歷實現思想如下:
對當前節點的左子樹進行后序遍歷;
對當前節點的右子樹進行后序遍歷;
訪問根節點;
如下圖所示:
遞歸方式實現如下:
const bt = require('./tree') // 遞歸版 function postorder(root) { if (!root) return postorder(root.left) postorder(root.right) console.log(root.val) } postorder(bt) /** 結果 D E B H I F G C A */
迭代方式實現如下:
// 非遞歸版 function postorder(root) { if (!root) return const outputStack = [] const stack = [root] while (stack.length) { const node = stack.pop() outputStack.push(node) // 這里先入left需要保證left后出,在stack中后出,就是在outputStack棧中先出 node.left && stack.push(node.left) node.right && stack.push(node.right) } while (outputStack.length) { const node = outputStack.pop() console.log(node.val) } } postorder(bt) /** 結果 D E B H I F G C A */
關于二叉樹及各種遍歷算法相關內容已講述完畢。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/127772.html
摘要:本篇主要介紹二叉樹的概念二叉樹的表示二叉樹的操作三種遍歷方式實現求二叉樹的子樹求節點的父節點二叉樹高度,可能是考試中的,也可能是面試中的。通常二叉樹的遍歷根據根節點的遍歷次序分為先根遍歷中根遍歷后根遍歷。 聲明:碼字不易,轉載請注明出處,歡迎文章下方討論交流。 前言:Java數據結構與算法專題會不定時更新,歡迎各位讀者監督。本篇主要介紹二叉樹的概念、二叉樹的表示、二叉樹的操作(三種遍歷...
摘要:左子樹的加法運算結果為,右子樹的減法運算結果為。如圖,該圖說明了隨著每個新的字符被讀入后該解析樹的內容和結構。使函數走向基點的遞歸過程就是調用求值函數計算當前節點的左子樹右子樹的值。最后,我們將在圖中創建的解析樹上遍歷求值。 解析樹 完成樹的實現之后,現在我們來看一個例子,告訴你怎么樣利用樹去解決一些實際問題。在這個章節,我們來研究解析樹。解析樹常常用于真實世界的結構表示,例如句子或數...
摘要:區塊體則包括當前區塊經過驗證的區塊創建過程中生成的所有交易記錄。假如要驗證區塊結構圖中交易,節點會通過向相鄰節點索要通過消息包括從交易哈希值沿樹上溯至區塊頭根哈希處的哈希序列即哈希節點稱為認證路徑來確認交易的存在性和正確性。 本文首發于深入淺出區塊鏈社區原文鏈接:比特幣區塊結構Merkle樹及簡單支付驗證分析原文已更新,請讀者前往原文閱讀 在比特幣網絡中,不是每個節點都有能力儲存完整的...
摘要:題目鏈接題目分析在二叉樹中,若兩個葉子節點的層數相同,但具有不同的父節點,那么這兩個節點互為節點。給定一個二叉樹及兩個節點,返回兩個節點在二叉樹中,是否互為節點。遍歷完成后,直接判斷數組中對應的值是否相同即可。 D76 993. Cousins in Binary Tree 題目鏈接 993. Cousins in Binary Tree 題目分析 在二叉樹中,若兩個葉子節點的層數相同...
閱讀 547·2023-03-27 18:33
閱讀 732·2023-03-26 17:27
閱讀 630·2023-03-26 17:14
閱讀 591·2023-03-17 21:13
閱讀 521·2023-03-17 08:28
閱讀 1801·2023-02-27 22:32
閱讀 1292·2023-02-27 22:27
閱讀 2178·2023-01-20 08:28