關於二元樹的建立和遍歷,本文中做出了詳細的介紹,以及前序二叉樹遍歷、中序二叉樹遍歷、後序二叉樹遍歷的演算法也做出了解釋,並引用了程式碼,是為了讓大家看的更清晰。本文的介紹還是先從二元樹和二元查找樹開始吧,方便理解。 apache php mysql
二元樹and二叉查找樹
#關於樹的相關術語:
#節點:樹中的每個元素稱為一個節點,
根節點: 位於整棵樹頂點的節點,它沒有父節點, 如上圖5#
#子節點: 其他節點的後代葉子節點: 沒有子節點的元素稱為葉子節點, 如上圖3 8 24二元樹:二元樹就是一種資料結構, 它的組織關係就像是自然界中的樹一樣。官方語言的定義是:是一個有限元素的集合,該集合或者為空、或者由一個稱為根的元素及兩個不相交的、被分別稱為左子樹和右子樹的二叉樹組成。 二元查找樹:二元查找樹也叫二元搜尋樹(BST),它只允許我們在左節點儲存比父節點更小的值,右節點儲存比父節點更大的數值,上圖展示的就是一顆二元查找樹。
function BinarySearchTree () { var Node = function(key) { this.key = key, this.left = null, this.right = null } var root = null }它還應該有一些方法:
- insert(key) 插入一個新的按鍵
- inOrderTraverse() 對樹進行中序遍歷,並列印結果
- preOrderTraverse() 對樹進行先序遍歷,並列印結果
- postOrderTraverse() 對樹進行後序遍歷,並列印結果
- search(key) 尋找樹中的鍵,如果存在回傳true,不存在回傳fasle
- findMin() 傳回樹中的最小值
- findMax() 傳回樹中的最大值
- #remove(key) 刪除樹中的某個鍵
this.insert = function(key) { var newNode = new Node(key) if(root === null) { root = newNode } else { insertNode(root, newNode) } }定義一下insertNode() 方法,這個方法會透過遞歸得呼叫自身,來找到新新增節點的適當位置
var insertNode = function(node, newNode) { if (newNode.key <= node.key) { if (node.left === null) { node.left = newNode }else { insertNode(node.left, newNode) } }else { if (node.right === null) { node.right = newNode }else { insertNode(node.right, newNode) } } }完成中序遍歷方法要實作中序遍歷,我們需要一個inOrderTraverseNode(node)方法,它可以遞歸呼叫自身來遍歷每個節點
this.inOrderTraverse = function() { inOrderTraverseNode(root) }這個方法會印出每個節點的key值,它需要一個遞歸終止條件--檢查傳入的node是否為null,如果不為空,就繼續遞歸呼叫自身檢查node的left、 right節點
實作起來也很簡單:
var inOrderTraverseNode = function(node) { if (node !== null) { inOrderTraverseNode(node.left) console.log(node.key) inOrderTraverseNode(node.right) } }先序遍歷、後序遍歷有了中序遍歷的方法,只要稍作改動,就可以實現先序遍歷與後序遍歷了
上程式碼:
// 实现先序遍历 this.preOrderTraverse = function() { preOrderTraverseNode(root) } var preOrderTraverseNode = function(node) { if (node !== null) { console.log(node.key) preOrderTraverseNode(node.left) preOrderTraverseNode(node.right) } } // 实现后序遍历 this.postOrderTraverse = function() { postOrderTraverseNode(root) } var postOrderTraverseNode = function(node) { if (node !== null) { postOrderTraverseNode(node.left) postOrderTraverseNode(node.right) console.log(node.key) } }發現了吧,其實就是內部語句更換了前後位置,這也剛好符合三種遍歷規則:先序遍歷(根-左-右)、中序遍歷(左-根-右)、中序遍歷(左-右-根)先來做個測試吧現在的完整程式碼如下:
function BinarySearchTree () { var Node = function(key) { this.key = key, this.left = null, this.right = null } var root = null //插入节点 this.insert = function(key) { var newNode = new Node(key) if(root === null) { root = newNode } else { insertNode(root, newNode) } } var insertNode = function(node, newNode) { if (newNode.key <= node.key) { if (node.left === null) { node.left = newNode }else { insertNode(node.left, newNode) } }else { if (node.right === null) { node.right = newNode }else { insertNode(node.right, newNode) } } } //实现中序遍历 this.inOrderTraverse = function() { inOrderTraverseNode(root) } var inOrderTraverseNode = function(node) { if (node !== null) { inOrderTraverseNode(node.left) console.log(node.key) inOrderTraverseNode(node.right) } } // 实现先序遍历 this.preOrderTraverse = function() { preOrderTraverseNode(root) } var preOrderTraverseNode = function(node) { if (node !== null) { console.log(node.key) preOrderTraverseNode(node.left) preOrderTraverseNode(node.right) } } // 实现后序遍历 this.postOrderTraverse = function() { postOrderTraverseNode(root) } var postOrderTraverseNode = function(node) { if (node !== null) { postOrderTraverseNode(node.left) postOrderTraverseNode(node.right) console.log(node.key) } } }竟然已經完成了新增節點和遍歷的方式,我們來測試一下:#定義一個陣列,裡面有一些元素
var arr = [9,6,3,8,12,15]我們將arr中的每個元素依此插入到二元搜尋樹中,然後列印結果
var tree = new BinarySearchTree() arr.map(item => { tree.insert(item) }) tree.inOrderTraverse() tree.preOrderTraverse() tree.postOrderTraverse()運行程式碼後,我們先來看看插入節點後整顆樹的情況:
3<br>6<br>#8<br>9<br>12<br>15<br><br>
9<br>6<br>3<br>8<br>12 <br>15<br><br>
3<br>8<br>6<br>15<br>12<br>9<br> <br>
找出最小、最大值怎麼做呢?其實只需要將根節點傳入minNode/或maxNode方法,然後透過循環判斷node為左側(minNode)/右側(maxNode)的節點為null
##實作程式碼:
// 查找最小值 this.findMin = function() { return minNode(root) } var minNode = function(node) { if (node) { while (node && node.left !== null) { node = node.left } return node.key } return null } // 查找最大值 this.findMax = function() { return maxNode(root) } var maxNode = function (node) { if(node) { while (node && node.right !== null) { node =node.right } return node.key } return null }
所搜特定值
this.search = function(key) { return searchNode(root, key) }
同样,实现它需要定义一个辅助方法,这个方法首先会检验node的合法性,如果为null,直接退出,并返回fasle。如果传入的key比当前传入node的key值小,它会继续递归查找node的左侧节点,反之,查找右侧节点。如果找到相等节点,直接退出,并返回true
var searchNode = function(node, key) { if (node === null) { return false } if (key < node.key) { return searchNode(node.left, key) }else if (key > node.key) { return searchNode(node.right, key) }else { return true } }
移除节点
移除节点的实现情况比较复杂,它会有三种不同的情况:
需要移除的节点是一个叶子节点
需要移除的节点包含一个子节点
需要移除的节点包含两个子节点
和实现搜索指定节点一元,要移除某个节点,必须先找到它所在的位置,因此移除方法的实现中部分代码和上面相同:
// 移除节点 this.remove = function(key) { removeNode(root,key) } var removeNode = function(node, key) { if (node === null) { return null } if (key < node.key) { node.left = removeNode(node.left, key) return node }else if(key > node.key) { node.right = removeNode(node.right,key) return node }else{ //需要移除的节点是一个叶子节点 if (node.left === null && node.right === null) { node = null return node } //需要移除的节点包含一个子节点 if (node.letf === null) { node = node.right return node }else if (node.right === null) { node = node.left return node } //需要移除的节点包含两个子节点 var aux = findMinNode(node.right) node.key = aux.key node.right = removeNode(node.right, axu.key) return node } } var findMinNode = function(node) { if (node) { while (node && node.left !== null) { node = node.left } return node } return null }
其中,移除包含两个子节点的节点是最复杂的情况,它包含左侧节点和右侧节点,对它进行移除主要需要三个步骤:
需要找到它右侧子树中的最小节点来代替它的位置
将它右侧子树中的最小节点移除
将更新后的节点的引用指向原节点的父节点
有点绕儿,但必须这样,因为删除元素后的二叉搜索树必须保持它的排序性质
测试删除节点
tree.remove(8) tree.inOrderTraverse()
打印结果:
3<br>6<br>9<br>12<br>15<br>
8 这个节点被成功删除了,但是对二叉查找树进行中序遍历依然是保持排序性质的
到这里,一个简单的二叉查找树就基本上完成了,我们为它实现了,添加、查找、删除以及先中后三种遍历方法
存在的问题
但是实际上这样的二叉查找树是存在一些问题的,当我们不断的添加更大/更小的元素的时候,会出现如下情况:
tree.insert(16) tree.insert(17) tree.insert(18)
来看看现在整颗树的情况:
看图片容易得出它是不平衡的,这又会引出平衡树的概念,要解决这个问题,还需要更复杂的实现,例如:AVL树,红黑树 哎,之后再慢慢去学习吧
相关文章:
以上是js_前中後序二元樹遍歷的三種演算法_簡單二元樹的實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

WebStorm Mac版
好用的JavaScript開發工具

Dreamweaver Mac版
視覺化網頁開發工具

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

禪工作室 13.0.1
強大的PHP整合開發環境