二叉搜索樹的創建
定義Node
爲節點類,Tree
爲二叉樹,設置方法insert
用於增加樹上的結點,根據節點和樹上已有結點的權值大小確定插入位置。
/* 結點 */
function Node(val, left, right) {
this.val = val;
this.left = null;
this.right = null;
}
/* 樹 */
function Tree() {
this.root = null;
}
/* 結點插入二叉樹 */
Tree.prototype.insert = function (val) {
/* 創建新節點 */
let newNode = new Node(val, null, null);
/* 樹根爲空直接賦值 */
if (!this.root) {
this.root = newNode;
} else {
let cur = this.root;
/* 找到位置才能退出循環 */
while (true) {
if (newNode.val > cur.val) {
/* 右子樹爲空,直接將新節點作爲右孩子 */
if (!cur.right) {
cur.right = newNode;
break;
} else {
cur = cur.right;
}
}
if (newNode.val < cur.val) {
if (!cur.left) {
cur.left = newNode;
break;
} else {
cur = cur.left;
}
}
}
}
}
let tree = new Tree();
let arr = [4, 6, 5, 2, 3, 1];
arr.forEach(v => tree.insert(v));
遞歸方式實現三種遍歷
遞歸調用,選擇對應時機輸出即可,缺點是太耗性能.
var orderTraversal = function (root) {
let res = [];
function order(rt) {
if (rt) {
res.push(rt.val);//前序
order(rt.left);
// res.push(rt.val);//中序
order(rt.right);
// res.push(rt.val);//後序
}
}
order(root);
return res;
}
前序遍歷
實現: 彈出棧頂元素node
,將棧頂元素的右結點right
,左結點left
放到棧裏面,然後取出棧頂元素left
作爲父結點重複該操作,只有left
下的所有結點全部輸出完,right
纔會進行輸出,從而實現根->左->右
的輸出順序。
leetcode:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/
每次循環stack變化:
[4]--------->4出棧,6和2進棧
[6,2]------->2出棧,3和1進棧
[6,3,1]----->1出棧
[6,3]------->3出棧
[6]--------->6出棧,5進棧
[5]--------->5出棧
/* 根->左->右 */
var preorderTraversal = function (root) {
if (!root) return [];
let res = [], stack = [root];
/* 設置棧存序列 */
while (stack.length) {
let node = stack.pop();/* 棧頂元素 */
res.push(node.val);
/* 後進先出,左邊的在後面進入,確保每次先彈出左邊結點 */
if (node.right) stack.push(node.right);
if (node.left) stack.push(node.left);
}
return res;
};
console.log(preorderTraversal(tree.root));//[ 4, 2, 1, 3, 6, 5 ]
中序遍歷
實現: 用棧來存儲結點,只有結點左子樹全部輸出完該結點纔可能輸出,查到沒有左孩子的結點node
進行輸出,然後再到node的右子樹
進行同樣的操作.
leetcode:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
/* 左->根->右 */
var inorderTraversal = function (root) {
if (!root) return [];
let res = [], stack = [], parent = root;
while (stack.length || parent) {
/* 一直往左下找,直到最左下方結點 */
while (parent) {
stack.push(parent);
parent = parent.left;
}
let node = stack.pop();//彈出最左下方結點
res.push(node.val);
parent=node.right;//完成(左->根),對該結點右下方的結點進行遍歷
}
return res;
};
console.log(inorderTraversal(tree.root));//[ 1, 2, 3, 4, 5, 6 ]
後序遍歷
實現: 在中序遍歷的基礎上添加一個判斷,只有右子樹全部輸出才能輸出當前結點。
leetcode:https://leetcode-cn.com/problems/binary-tree-postorder-traversal/
/* 左->右->根 */
var postorderTraversal = function (root) {
if (!root) return [];
let set = new Set();//記錄結點是否被訪問過
let stack = [], res = [], parent = root;
while (stack.length || parent) {
while (parent) {
stack.push(parent);
parent = parent.left;
}
let node = stack[stack.length - 1];/* 棧頂元素 */
/* 對還未輸出的右子樹進行遍歷輸出 */
if (node.right && !set.has(node.right)) {
set.add(node.right);//標記node.right下的子樹已經輸出
parent = node.right;
} else {
res.push(node.val);
stack.pop();
}
}
return res;
}
console.log(postorderTraversal(tree.root));//[ 1, 3, 2, 5, 6, 4 ]