**二叉樹是一種數據結構。其特色是:javascript
1.由一系列節點組成,具備層級結構。每一個節點的特性包含有節點值、關係指針。節點之間存在對應關係。java
2.樹中存在一個沒有父節點的節點,叫作根節點。樹的末尾存在一系列沒有子節點的節點,稱爲葉子節點。其餘能夠叫作中間節點。node
3.樹的根節點位於第一層,層級數越大,節點位置越深,層級數也叫作樹高。算法
**排序二叉樹爲二叉樹的一種類型,其特色是:數據結構
1.節點分爲左右子樹。this
2.在不爲空的狀況下,左子樹子節點的值都小於父節點的值。spa
3.在不爲空的狀況下,右子樹子節點的值都大於父節點的值。debug
4.每一個節點的左右子樹都按照上述規則排序。
指針
// 將值生成節點,節點包括:節點值、左指針、右指針 class Node { constructor(key) { this.key = key; this.left = null; this.right = null; } }
// 二叉樹 class BinaryTree { constructor() { this.root = null; // 根節點 } // 插入,插入的是一個節點,因此應該先把值生成節點(包括節點值,左指針,右指針) insert(key) { const newNode = new Node(key); // 若是根節點爲空,則新節點做爲根節點,不然在根節點下進行插入 if (this.root === null) { this.root = newNode; } this.insertNode(this.root, newNode); } // 有根節點的狀況下插入值 insertNode(root, newNode) { if (newNode.key < root.key) { // 進入左子樹 if (root.left === null) { // 左子樹爲空 root.left = newNode; } else { // 左子樹已存在 this.insertNode(root.left, newNode); } } else if (newNode.key > root.key) { // 進入右子樹 if (root.right === null) { // 右子樹爲空 root.right = newNode; } else { // 右子樹已存在 this.insertNode(root.right, newNode); } } } }
const binaryTree = new BinaryTree(); var keys = [19, 8, 15, 24, 45, 12, 5]; keys.forEach((key) => binaryTree.insert(key)); console.log(binaryTree);
// 中序遍歷:遞歸方法 var inorderTraversal = function (root) { var result = []; pushRoot(root, result); return result; }; function pushRoot(root, result) { if (root !== null) { // 左 if (root.left !== null) { pushRoot(root.left, result); } // 中 result.push(root.key); // 右 if (root.right !== null) { pushRoot(root.right, result); } } } // 注意這裏傳入的是binaryTree.root,而不是binaryTree inorderTraversal(binaryTree.root); // [5, 8, 12, 15, 19, 24, 45]
var inorderTraversal = function (root) { let result = []; let stack = []; let node = root; while (node !== null || stack.length !== 0) { if (node !== null) { stack.push(node); node = node.left; // 遍歷左,存入棧中 } else { debugger; // node===null時說明左邊沒有了,那麼棧頂就是最左邊的(最小的) node = stack.pop(); result.push(node.key); node = node.right; //看右邊有沒有 } } console.log(result); // [5, 8, 12, 15, 19, 24, 45] return result; }; inorderTraversal(binaryTree.root);
// 前序遍歷 遞歸 var preOrderTraversal = function (root) { var res = []; preOrder(root, res); return res; }; function preOrder(root, res) { if (root !== null) { // 中 res.push(root.key); // 左 preOrder(root.left, res); // 右 preOrder(root.right, res); } } console.log(preOrderTraversal(binaryTree.root)); // [19, 8, 5, 15, 12, 24, 45]
var preOrderTraversal = function (root) { var res = []; var stack = []; var node = root; while (node !== null || stack.length !== 0) { if (node !== null) { res.push(node.key); stack.push(node); node = node.left; } else { node = stack.pop(); node = node.right; } } return res }; console.log(preOrderTraversal(binaryTree.root)) // [19, 8, 5, 15, 12, 24, 45]
var afterOrderTraversal = function (root) { var res = []; afterOrder(root, res); return res; }; function afterOrder(root, res) { if (root !== null) { // 左 afterOrder(root.left, res); // 右 afterOrder(root.right, res); // 中 res.push(root.key); } } console.log(afterOrderTraversal(binaryTree.root)); // [5, 12, 15, 8, 45, 24, 19]
首先要搞清楚先序、中序、後序的非遞歸算法共同之處:用棧來保存先前走過的路徑,以即可以在訪問完子樹後,能夠利用棧中的信息,回退到當前節點的雙親節點,進行下一步操做。code
後序遍歷的非遞歸算法是三種順序中最複雜的,緣由在於,後序遍歷是先訪問左、右子樹,再訪問根節點,而在非遞歸算法中,利用棧回退到時,並不知道是從左子樹回退到根節點,仍是從右子樹回退到根節點,若是從左子樹回退到根節點,此時就應該去訪問右子樹,而若是從右子樹回退到根節點,此時就應該訪問根節點。因此相比前序和後序,必須得在壓棧時添加信息,以便在退棧時能夠知道是從左子樹返回,仍是從右子樹返回進而決定下一步的操做。