二叉樹是一種數據結構。其特點是:
1.由一系列節點組成,具有層級結構。每個節點的特性包含有節點值、關系指針。節點之間存在對應關系。
2.樹中存在一個沒有父節點的節點,叫做根節點。樹的末尾存在一系列沒有子節點的節點,稱為葉子節點。其他可以叫做中間節點。
3.樹的根節點位於第一層,層級數越大,節點位置越深,層級數也叫做樹高。
排序二叉樹為二叉樹的一種類型,其特點是:
1.節點分為左右子樹。
2.在不為空的情況下,左子樹子節點的值都小於父節點的值。
3.在不為空的情況下,右子樹子節點的值都大於父節點的值。
4.每個節點的左右子樹都按照上述規則排序。
如圖:
(打錯字了..)
js代碼來實現上述數據結構:
1.節點用對象來描述,節點特性用對象屬性來描述。
1 class Node { 2 constructor(key) { 3 this.key = key;// 節點值 4 this.left = null;// 左指針 5 this.right = null;// 右指針 6 } 7 }
2.二叉樹結構用對象來描述。
1 // 二叉樹 2 class BinaryTree { 3 constructor() { 4 this.root = null;// 根節點 5 } 6 insert(key) {// api--插入 7 const newNode = new Node(key); 8 if (this.root === null) {// 設置根節點 9 this.root = newNode; 10 } 11 method.insertNode(this.root, newNode); 12 } 13 }
相關方法:
1 // method 2 method = { 3 insertNode(root, newNode) { 4 if (newNode.key < root.key) {// 進入左子樹 5 if (root.left === null) {// 左子樹為空 6 root.left = newNode; 7 } else {// 左子樹已存在 8 method.insertNode(root.left, newNode); 9 } 10 } else if (newNode.key > root.key) {// 進入右子樹 11 if (root.right === null) {// 右子樹為空 12 root.right = newNode; 13 } else {// 右子樹已存在 14 method.insertNode(root.right, newNode); 15 } 16 } 17 } 18 };
具體用法:
1 // 實例化二叉樹 2 const binaryTree = new BinaryTree(); 3 4 // key值 5 const keys = [19, 8, 15, 24, 45, 12, 5]; 6 7 // 生成排序二叉樹 8 keys.forEach(key => binaryTree.insert(key));
結果:
排序二叉樹的遍歷:
一、中序遍歷
(1)以上圖為例,中序遍歷順序為: 5 - 8 - 12 - 15 - 19 - 24 - 45。
(2)總是先遍歷左子樹,然后訪問根節點,接着遍歷右子樹。
代碼實現:
1 class BinaryTree { 2 ... 3 // callback為訪問節點時執行的操作 4 inorderTraversal(callback) {// api--中序遍歷 5 method.inorderTraversalNode(this.root, callback); 6 } 7 } 8 9 method = { 10 ... 11 12 inorderTraversalNode(node, callback) { 13 if (node) {// 當前節點 14 method.inorderTraversalNode(node.left, callback);// 遍歷左子樹 15 callback(node);// 訪問節點 16 method.inorderTraversalNode(node.right, callback);// 遍歷右子樹 17 } 18 }, 19 }; 20 21 // 中序遍歷 22 binaryTree.inorderTraversal(node => console.log(node.key));
1 // key值 2 const keys = [19, 8, 15, 24, 45, 12, 5];
輸入結果:5 - 8 - 12 - 15 - 19 - 24 - 45
二、前序遍歷
(1)以上圖為例,前序遍歷順序為: 19 - 8 - 5 - 15 - 12 - 24 - 45。
(2)總是先訪問根節點,然后遍歷左子樹,接着遍歷右子樹。
代碼實現:
1 class BinaryTree { 2 ... 3 preOrderTraversal(callback) {// api--前序遍歷 4 method.preOrderTraversalNode(this.root, callback); 5 } 6 } 7 8 method = { 9 ... 10 preOrderTraversalNode(node, callback) { 11 if (node) {// 當前節點 12 callback(node);// 訪問節點 13 method.preOrderTraversalNode(node.left, callback);// 遍歷左子樹 14 method.preOrderTraversalNode(node.right, callback);// 遍歷右子樹 15 } 16 } 17 }; 18 19 // 前序遍歷 20 binaryTree.preOrderTraversal(node => console.log(node.key));
1 // key值 2 const keys = [19, 8, 15, 24, 45, 12, 5];
輸入結果:19 - 8 - 5 - 15 - 12 - 24 - 45
三、后序遍歷
(1)以上圖為例,后序遍歷順序為: 5 - 12 - 15 - 8 - 45 - 24 - 19。
(2)先遍歷左子樹,接着遍歷右子樹,最后訪問根節點。
代碼實現:
1 class BinaryTree { 2 ... 3 postOrderTraversal(callback) {// api--后序遍歷 4 method.postOrderTraversalNode(this.root, callback); 5 } 6 } 7 8 method = { 9 ... 10 postOrderTraversalNode(node, callback) { 11 if (node) {// 當前節點 12 method.postOrderTraversalNode(node.left, callback);// 遍歷左子樹 13 method.postOrderTraversalNode(node.right, callback);// 遍歷右子樹 14 callback(node);// 訪問節點 15 } 16 } 17 }; 18 19 // 后序遍歷 20 binaryTree.postOrderTraversal(node => console.log(node.key));
1 // key值 2 const keys = [19, 8, 15, 24, 45, 12, 5];
輸入結果:5 - 12 - 15 - 8 - 45 - 24 - 19
排序二叉樹的查找:
(1)查找最大值。根據排序二叉樹的特點,右子樹的值都大於父節點的值。只需要進入節點的右子樹查找,當某個節點的右子樹為空時,該節點就是最大值節點。
代碼實現:
1 class BinaryTree { 2 ... 3 max() { 4 return method.maxNode(this.root); 5 } 6 } 7 8 method = { 9 ... 10 maxNode(node) { 11 if (node) { 12 while(node.right !== null) {// 右子樹不為空時 13 node = node.right; 14 } 15 return node.key; 16 } 17 return null; 18 } 19 };
1 // key值 2 const keys = [19, 8, 15, 24, 45, 12, 5];
結果:
(2)查找最小值。根據排序二叉樹的特點,左子樹的值都小於父節點的值。只需要進入節點的左子樹查找,當某個節點的左子樹為空時,該節點就是最小值節點。
代碼實現:
1 class BinaryTree { 2 ... 3 min() { 4 return method.minNode(this.root); 5 } 6 } 7 8 method = { 9 ... 10 minNode(node) { 11 if (node) { 12 while(node.left!== null) {// 左子樹不為空時 13 node = node.left; 14 } 15 return node.key; 16 } 17 return null; 18 } 19 };
1 // key值 2 const keys = [19, 8, 15, 24, 45, 12, 5];
結果:
(3)查找給定值。在排序二叉樹中查找有沒有節點對應的值與給定值相同。
根據排序二叉樹的特點,比較給定值與節點值,小於時進入節點左子樹。大於時進入節點右子樹。等於時返回真。層層對比,最后如果子樹為空,則表示沒有找到。
代碼實現:
1 class BinaryTree { 2 ... 3 search(key) { 4 return method.searchNode(this.root, key); 5 } 6 } 7 8 method = { 9 ... 10 searchNode(node, key) { 11 if (node === null) {// 沒有找到 12 return false; 13 } 14 if (key < node.key) {// 進入左子樹 15 return method.searchNode(node.left, key); 16 } else if (key > node.key) {// 進入右子樹 17 return method.searchNode(node.right, key); 18 } else {// 找到了 19 return true; 20 } 21 } 22 };
1 // key值 2 const keys = [19, 8, 15, 24, 45, 12, 5];
結果:
排序二叉樹的刪除:
當刪除的節點為葉子節點時,直接把葉子節點設置成空。如圖:刪除值為5的節點。
原:
現:
當刪除的節點存在左子樹時,把父節點的關系指針直接指向左子樹。如圖:刪除值為15的節點。存在右子樹時同理。
原:
現:
當節點存在左右子樹時,先去右子樹里找到最小值,然后用最小值替換節點值,最后進入右子樹刪除最小值對應的節點。如圖:刪除值為8的節點。
原:
現:
代碼實現:
1 class BinaryTree { 2 ... 3 remove(key) { 4 this.root = method.removeNode(this.root, key); 5 } 6 } 7 8 method = { 9 ... 10 removeNode(node, key) { 11 if(node === null) {// 沒有找到值對應的節點 12 return null; 13 } 14 if (key < node.key) {// 給定值小於當前節點值 15 node.left = method.removeNode(node.left, key); 16 return node; 17 } else if (key > node.key) {// 給定值大於當前節點值 18 node.right = method.removeNode(node.right, key); 19 return node; 20 } else {// 找到給定值對應的節點 21 if (node.left === null && node.right === null) {// 節點為葉子節點 22 node = null; 23 return node; 24 } 25 26 if (node.left === null) {// 節點存在右子樹 27 node = node.right; 28 return node; 29 } else if (node.right === null) {// 節點存在左子樹 30 node = node.left; 31 return node; 32 } 33 34 // 節點存在左右子樹時,先去右子樹里找到最小值,然后用最小值替換節點值,最后進入右子樹刪除最小值對應的節點。 35 const minKey = method.minNode(node.right); 36 node.key = minKey; 37 method.removeNode(node.right, minKey); 38 return node; 39 } 40 } 41 };
結果:
累死我了。。。