二叉树的遍历-前中后层序,二叉搜索树

二叉树-百度百科:

二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个结点最多只能有两棵子树,且有左右之分 。

二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个结点 。

二叉树

二叉树的性质:

  • (i)层最多有(2^{i - 1})个节点
  • 二叉树深度为(k),最多有(2^{k - 1})个节点
  • 满二叉树:上图中即为一个满二叉树
  • 完全二叉树:出最后一层外为满二叉树,最后一层叶子节点自左向右依次排列
    完全二叉树
二叉树的遍历
//ES6	
class Node {
    constructor(val){
        this.val = val;
        this.left = null;
        this.right = null;
    }
}
class BinaryTree {
    constructor() {
        this.root = null;
    }

    addNode(val) {
        let curNode = new Node(val);
        if (!this.root) this.root = curNode;
        else {//层序遍历,找到空节点加入
            let q = [this.root];
            while (q.length) {
                let cur = q.shift();
                if(cur.left) {
                    q.push(cur.left);
                } else {
                    cur.left = curNode;
                    break;
                }
                if (cur_right) {
                    q.push(cur_right);
                } else {
                    cur_right = curNode;
                    break;
                }
            }
            
        } 
    }
}
  • 前序遍历

    • 节点的访问顺序为: 根节点 --> 左子节点 --> 右子节点
    1. 先访问当前节点
    2. 访问当前节点的左子节点
    3. 重复2,直到当前节点为空,返回上一层状态,访问其右子节点,重复2
    • 递归实现
    preOrder(node) {
        if (node) {
            console.log(node.val);
            this.preOrder(node.left);
            this.preOrder(node.right);
        }
    }
    • 非递归实现

      + 利用栈的性质,先进后出,根据前序遍历的顺序 根 -> 左 -> 右
      
      1. 初始化栈,root入栈
      2. 栈顶元素出栈,记录其value
      3. 右子节点存在则入栈
      4. 左子节点存在则入栈
      5. 重复2,3,4直到栈空
      
    preOrder() {
        let goal = [],
            idx = 0,
            sta = [this.root];//stack
        while (sta.length) {
            let cur = sta.pop();
            goal[idx++] = cur.val;
            if (cur.right) sta.push(cur.right);
            if (cur.left) sta.push(cur.left);
        }
        console.log(goal);
    }
  • 中序遍历:左子节点 -> 根节点 -> 右子节点

    • 递归写法同上,修改顺序即可
    • 非递归:
    1. 根节点入栈
    2. 判断栈顶元素左子节点是否存在
      1. 存在,继续入栈左子节点
      2. 不存在,弹栈,记录value,若有右子节点存在则入栈
    3. 重复2,直到栈空
    inOrder() {
        let goal = [],
            idx = 0,
            cur = this.root,
            sta = [];//stack
        if (!this.root) console.log("Empty!");
        while (sta.length || cur) {
            while (cur) {//存在则入栈
                sta.push(cur);
                cur = cur.left;
            }
            cur = sta.pop();//top
            goal[idx++] = cur.val;
            cur = cur.right;//修改cur为当前节点的右子节点,之后若存在则入栈
        }
        console.log(goal);
    }
  • 后序遍历: 左子节点 -> 右子节点 -> 根节点

    • 递归写法同上,修改顺序即可
    • 需要保证在左子树、右子树访问结束后在访问根节点 -- 加标记,记录根节点访问次数, == 2即弹出
    1. 根节点入栈
    2. 左子节点入栈 ...-> 叶子结点
    3. 弹栈,记录叶子结点,叶子结点父节点次数 + 1,右子节点入栈
    4. 栈顶继续2,3
//待填坑。。。
    • 法2:使用双栈,前序遍历压栈顺序为先右后左,其遍历结果为:根 -> 左 -> 右,若压栈顺序为先左后右则遍历结果为:根 -> 右 -> 左,恰好为后序遍历的倒叙,第一次遍历结果使用另一个栈存储,最后弹栈输出即可
    lastOrder() {
        let goal = [],
            sta1 = [this.root],
            sta2 = [];
        console.log("LastOrder:");
        while (sta1.length) {
            let cur = sta1.pop();
            sta2.push(cur.val);
            if (cur.left) sta1.push(cur.left);
            if (cur.right) sta1.push(cur.right);
        }
        while (sta2.length) {
            goal.push(sta2.pop());
        }
        console.log(goal);
    }
  • 层序遍历:使用队列即可,先进先出。初始root入队,每次记录队首信息,存储队首的左右子节点即可。
    leverOrder() {
        let goal = [],
            idx = 0,
            q = [this.root];
        console.log("LeverOrder:");
        while (q.length) {
            let cur = q.shift();//取出队首
            //按层遍历记录每次的q.length即为当前层的节点数
            // let len = q.length;
            // for (let i = 0; i < len; i++){
            //     let cur = q.shift();
            //     if(...) ...
            // }
            goal[idx++] = cur.val;
            if (cur.left) q.push(cur.left);
            if (cur.right) q.push(cur.right);
        }
        console.log(goal);
    }
  • 二叉搜索树:左子节点的值小于根节点的值,右子节点的值大于根节点的值
  • JavaScript实现
class BinarySearchTree {
    constructor () {
        this.root = null;
    }
    addNode(data) {//相对于父节点的值,左子节点小,右子节点大
        if (this.root === null) this.root = new Node(data);
        else {
            let curNode = new Node(data);
            let cur = this.root;
            while (true) {
                if (data < cur.val) {
                    if (!cur.left) {
                        cur.left = curNode;
                        break;
                    } else {
                        cur = cur.left;
                    }
                } else {
                    if (!cur.right) {
                        cur.right = curNode;
                        break;
                    } else {
                        cur = cur.right;
                    }
                }
            }
        }
    }
    inOrder() {//二叉搜索树的中序遍历结果为升序
        let cur = this.root,
            st = [],
            book = [];
        while (cur || st.length) {
            while (cur) {
                st.push(cur);
                cur = cur.left;
            }
            cur = st.pop();
            book.push(cur.val);
            cur = cur.right;
        }
        console.log(`BST: ${book}`);
    }
}
原文地址:https://www.cnblogs.com/honey-cat/p/14655331.html