首先创建一个BST类表示 二叉查找树,这个是红黑树,AVL树的基础,有必要熟练掌握的
let Stack = require("./Stack").stack;
function TreeNode(data,left,right) {
this.data = (data === undefined ? 0 : data);
this.left = (left === undefined ? null : left);
this.right = (right === undefined ? null : right);
}
TreeNode.prototype.show = function() {
return this.data;
}
TreeNode.prototype.insert = insert;
function insert(data) {
let n = new TreeNode(data,null,null);
if(this.root == null) {
console.log("n is ",n);
this.root = n;
} else {
let current = this.root;
let parent;
while(true) {
parent = current;
if(data < current.data) {
current = current.left;
if(current === null) {
parent.left = n;
break;
}
} else {
current = current.right;
if(current === null) {
parent.right = n;
break;
}
}
}
}
}
/** 二叉树的中序遍历非递归方式 也就是栈的方式 */
function middleOrder() {
const res = [];
const stk = [];
let root = this.root;
while(root || stk.length) {
while(root) {
stk.push(root);
root = root.left;
}
root = stk.pop();
res.push(root.data);
root = root.right;
}
return res;
}
/**
* 二叉树的前序遍历非递归方式 */
function preOrder() {
const res = [];
const stack = new Stack();
stack.push(this.root);
while(!stack.empty()) {
let cur = stack.pop();
res.push(cur.data);
if(cur.right != null) {
stack.push(cur.right);
}
/** 最后再把左节点放入到栈中因为是先进后出的结构 */
if(cur.left != null) {
stack.push(cur.left);
}
}
return res;
}
/** 二叉树的后续遍历非递归方式 */
function afterOrder() {
const res = [];
const stack = new Stack();
let root = this.root;
stack.push(root);
while(!stack.empty()) {
let p = stack.pop();
res.push(p.data);
if(p.left != null) {
stack.push(p.left);
}
if(p.right !== null) {
stack.push(p.right);
}
}
return res.reverse();
}
function BST() {
this.root = null;
this.insert = insert;
this.middleOrder = middleOrder;
this.preOrder = preOrder;
this.afterOrder = afterOrder;
}
// let nums = new BST();
module.exports.BST = BST;
这里用的Stack 是之前写的栈数据结构:
function Stack() {
this.dataStore = [];
this.top = 0;
this.push = push;
this.add = push;
this.pop = pop;
this.peek = peek;
this.empty = empty;
}
function push(element) {
this.dataStore[this.top++] = element;
}
function pop() {
return this.dataStore[--this.top];
}
function peek() {
let targetIndex = this.top - 1;
if(targetIndex >= 0) {
return this.dataStore[this.top - 1];
}
}
function empty() {
return this.top === 0;
}
module.exports.stack = Stack;
上面的三种深度遍历方式(DFS): 先序遍历,中序遍历,后序遍历的非递归方式都用到了栈用来保存从根节点到叶子节点的路径,然后一层一层弹出迭代就成功完成了遍历。
1:先看先序遍历:首先输出根节点 -> 左子节点 -> 右子节点
难免就是需要将root添加到栈中,每次迭代就把根节点pop出来,然后检查该节点的左子节点和右子节点是否存在,如果存在就把该子节点放到栈里面(以此节点为根节点再检查左右子节点循环下去)知道栈被清空
const stack = new Stack();
stack.push(this.root);
while(!stack.empty()) {
let cur = stack.pop();
res.push(cur.data);
if(cur.right != null) {
stack.push(cur.right);
}
/** 最后再把左节点放入到栈中因为是先进后出的结构 */
if(cur.left != null) {
stack.push(cur.left);
}
}
至于为什么先将right放到栈中是因为栈是一个先进后出的数据结构,而我们想要的是left节点先出来很自然的就把像上面那样写的。
2: 后序遍历:用到了一个小技巧: 先序遍历:根 -> 左 -> 右 后序遍历: 左 -> 右 -> 根 可以发现对后序遍历翻转的结果是:根 -> 右 -> 左 这样可以复用先序遍历的代码,于是代码自然就出来了:
let root = this.root;
stack.push(root);
while(!stack.empty()) {
let p = stack.pop();
res.push(p.data);
if(p.left != null) {
stack.push(p.left);
}
if(p.right !== null) {
stack.push(p.right);
}
}
return res.reverse();
同理为什么先push p.left就是因为我们先要的是右子节点。到最后需要reverse以下结果这样就是我们想要的后序遍历了
3: 中序遍历:左 -> 根 -> 右,此遍历方法根先序遍历和后序遍历略有不同,但是思想都是相同的都是用的栈结构:
首先需要遍历到最左边的节点,并且所经过的节点都放入到栈里面,输出该节点的data,然后指针指向该节点右子节点(如果有继续找左子节点循环下去直到找到最左边的节点)
代码如下:
const res = [];
const stk = [];
let root = this.root;
while(root || stk.length) {
while(root) {
stk.push(root);
root = root.left;
}
root = stk.pop();
res.push(root.data);
root = root.right;
}
return res;