二叉树-Javascript实现

部分参考这篇博文

github代码

其实这里写的也就是原代码,学数据结构很难,二叉树各种操作的核心,我目前领悟到的就是递归,递归,递归,学习要学习自己觉得很困难的知识,这样才会有进步。恩,这个鸡汤真馊!!

/**
     *
     * @param val
     * @constructor
     */
    function Node(val, left, right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
    Node.prototype.show = function() {
        return this.val;
    }
    /**
     *
     * @constructor
     */
    function BTree() {
        this.root = null;
        this.depthBT = 0;
    }
    BTree.prototype = {
        constructor : BTree,
        add : function(val){
            // 插入二叉树,找准规律,
            // 1. 二叉树是值小的存放在左子树,值大的存放在右子树
            // 2. 插入的时候,用根节点举例,是当一个节点的左子树或者右子树为空的时候才可以插入
            // 3. 根据特点,可以得出大概思路,首先从根节点出发,判断是在根节点的左边还是右边,假设是在右边
            // 4. 判断根节点的右子树是否为空,如果为空,则把当前值放入右子树
            // 5. 如果不为空,则说明不能插入,需要把这个右子树设置为根节点,继续进行比较
            var newNode = new Node(val, null, null);
            if(!this.root) {
                // 根节点初始化
                this.root = newNode;
            }else {
                var current = this.root,
                    parent;// 保存current的值,表示父节点
                while(current) {
                    parent = current;
                    if(val < current.val) {
                        // 判断是否是底层节点
                        current = current.left;
                        if(current == null ) {
                            parent.left = newNode;
                            return;
                        }
                    }else {
                        current = current.right;
                        if(current == null) {
                            parent.right = newNode;
                            return;
                        }
                    }
                }
            }
        },
        depth : function(node) {
            // 求最大深度,是从最底层升序,即用递归,从顶层根节点向下延伸
            // 到达最底层后,再次调用node.left和node.right,则都return 0
            // 比较ldepth和rdepth,给最大的深度+1,这个1指的是这个子节点本身
            // 向上回溯递归,其实就相当于是比较子节点的深度,然后把自己分配到子节点深度最深的节点链上

            return !node ? 0 : Math.max(this.depth(node.left),this.depth(node.right)) + 1;
//            if(!node) {
//                return 0;
//            }else {
//                var ldepth = this.depth(node.left);
//                var rdepth = this.depth(node.right);
//                return ldepth > rdepth ? ldepth + 1 : rdepth + 1;
//            }
        },
        preorder : function(node) {
            // 举例:只有两层的二叉树23→16→45
            // 传入跟节点,由于先序遍历,直接打印当前节点的node值(23)
            // 传入左子树,作为新的node递归,先打印左子树的值(16),然后用左子树的.left递归,发现为空跳出
            // 左子树的.right递归,发现为空跳出
            // 传入右子树,作为新的node递归,先打印右子树的值(45),然后用右子树的.left递归,发现为空跳出
            // 右子树的.right递归,发现为空跳出

            // 先序遍历
            if(node) {
                console.log(node.show());
                this.preorder(node.left);
                this.preorder(node.right);
            }
        },
        inorderTraversal : function(node) {
            // 中序遍历
            if(node){
                this.inorderTraversal(node.left);
                console.log(node.show());
                this.inorderTraversal(node.right);
            }
        },
        postorderTraversal : function(node) {
            // 后序遍历
            if(node) {
                this.postorderTraversal(node.left);
                this.postorderTraversal(node.right);
                console.log(node.show());
            }
        },
        BFS : function(node) {
            // 1. 用数组模拟队列(先进先出),根节点入栈
            // 2. 只要队列不为空,就打印出队列头
            // 3. 判断节点左右子树是否各自为空,不为空则推入队列中

            // 广度优先遍历
            var nodes = [];

            if(!node) {
                console.log('empty tree');
            }else {
                nodes.push(node);

                while(nodes.length !== 0) {
                    node = nodes.shift();// 队首出栈
                    console.log(node.val);

                    if(node.left) {
                        nodes.push(node.left);
                    }
                    if(node.right) {
                        nodes.push(node.right);
                    }
                }
            }
        },
        searchMin : function(node) {
            // 最小值存储在左子树中,只需要一直访问左子树就可以了

            // 查找最小值
            while(node.left) {
                node = node.left;
            }
            return node;

        },
        searchMax : function(node) {
            // 查找最大值
            while(node.right) {
                node = node.right;
            }
            return node;
        },
        find : function(val) {
            // 需要判断查找值和根节点值的大小,决定继续访问左子树还是右子树

            // 查找指定值
            var node = this.root,
                parent = node;// 保存父节点

            while(node) {
                if(node.val === val) {
                    // 返回父节点对象和查找节点对象
                    return {nodeParent : parent,findNode : node};
                }else{
                    parent = node;
                    if(node.val > val) {
                        node = node.left;
                    }else {
                        node = node.right;
                    }
                }
            }
            return null;
        },
        removeNode : function(val) {
            // 1.删除节点没有子树,为叶子结点,则把父元素指向他的指针置为null
            // 2.删除节点有一个子树,则把父元素指向他的指针指向他的子树
            // 3.删除节点有两个子树,(1)用左子树中的最小值代替删除节点并删除最小值节点 (2)用右子树中的最大值代替删除节点并删除最大值节点

            // 删除节点
            var currentobj = this.find(val),
                parentNode,// 父元素
                currentNode;// 查找元素

            if(currentobj){
                parentNode = currentobj.nodeParent;// 获得父元素
                currentNode = currentobj.findNode;// 获得查找元素

                if(currentNode.left == null && currentNode.right == null){
                    choosePos(null);

                }else if(currentNode.left && currentNode.right){
                    var max = this.searchMax(currentNode.right).val,// 找到最大值节点
                        maxNode = this.find(max);// 找到最大值对象在二叉树中的父元素,以及本身

                    currentNode.val = max;// 将删除的节点的值改成最大值
                    maxNode.nodeParent.right = null;// 删除最大值节点,将最大值节点的父节点指向null

                }else if(currentNode.right){// 右节点存在,左节点不存在
                    choosePos(currentNode.right);
                }else if(currentNode.left) {// 左节点存在,右节点不存在
                    choosePos(currentNode.left);
                }
            }else{
                return '删除节点不再树中';
            }
            // 只是为了获得一个操作句柄,但是不知道怎么处理,只能写了一个方法,很笨
            function choosePos(setValue){
                currentNode.val > parentNode.val ? parentNode.right = setValue : parentNode.left = setValue;
            }
        }
    }

    var bt = new BTree(),
        arrTree = [23,45,16,37,3,4,99,22,44,52,42,10,100,1];

    arrTree.forEach(function(i){
        // 生成二叉树
        bt.add(i);
    });
    console.log(bt);

    console.log('先序');
    bt.preorder(bt.root);
    console.log('中序');
    bt.inorderTraversal(bt.root);
    console.log('后序');
    bt.postorderTraversal(bt.root);

    // 二叉树深度
    console.log(bt.depth(bt.root));

    // 查找二叉树最小值
    console.log('查找最小值 ' + bt.searchMin(bt.root).val);
    // 查找二叉树最大值
    console.log('查找最大值 ' + bt.searchMax(bt.root).val);

    // 查找指定值
    console.log('查找结果父元素的值:' + bt.find(99).nodeParent.val);
    console.log('查找结果的值:' + bt.find(99).findNode.val);

    // 删除节点
    console.log('删除节点之前查找结果父元素:' + bt.find(52).nodeParent.val);
    bt.removeNode(52);
    console.log('删除节点之后查找结果:' + bt.find(52));
    console.log(bt.removeNode(55));

目前实现

  • 二叉树插入
  • 二叉树最大深度
  • 二叉树深度优先遍历(先序,中序,后序)
  • 广度优先遍历
  • 二叉树查找最大值
  • 二叉树查找最小值
  • 二叉树查找指定值
  • 二叉树删除节点

你可能感兴趣的:(学习笔记,数据结构)