经由过程几道问题进修二叉搜刮树

二叉树人人都晓得,二叉搜刮树满足以下特性:

节点的左子树只包括小于当前节点的数

节点的右子树只包括大于当前节点的数

一切左子树和右子树本身必需也是二叉搜刮树

二叉搜刮树也叫二叉排序树,中序遍历二叉搜刮树的效果就是一次递增的遍历。

一、二叉搜刮树的竖立

相干问题:leetcode 108.将有序数组转换为二叉搜刮树 [中等]

那末如何将一个有序数组转换为一颗二叉搜刮树?

二叉搜刮树的每个分支的根节点都是他的中心值。依据这个特性,用二分法来将有序数组转换为一颗二叉搜刮树。

const sortedArrayToBST = nums => {
    // 边界条件
    if (nums.length === 0) {
        return null;
    } 
    if (nums.length === 1) {
        return new TreeNode(nums[0]);
    }
    // 向下取整获得中心值
    let mid = Math.floor(nums.length / 2);
    let root = new TreeNode(nums[mid]);
    // 递归 二分法
    root.left =  sortedArrayToBST(nums.slice(0, mid));
    root.right =  sortedArrayToBST(nums.slice(mid + 1));
    return root;
};

接下来我们考证下一棵树是不是满足二叉搜刮树。

二、考证二叉搜刮树

相干问题:leetcode 98.考证二叉搜刮树 [中等]

思绪就是,中序遍历假如满足递增的就行。

用一个max作为考证值的变量,用中序遍历前面的值和背面的值作比较,一向递增则满足二叉搜刮树。

const isValidBST = root => {
    let isValidBSTFlag = true;
    let max = -Number.MAX_VALUE;
    const orderSearch = root => {
        if (root) {
            orderSearch(root.left);
            if (root.val > max) {
                max = root.val;
            } else {
                isValidBSTFlag = false;
            }
            orderSearch(root.right);
        }
    }
    orderSearch(root);
    return isValidBSTFlag;
};

上一个非递归解法。

非递归中序遍历的思绪就是运用栈,将节点的左子树压入直到恭弘=叶 恭弘节点,然后操纵完左子树跟根节点后再操纵右子树。

循环反复,直到栈空。

const isValidBST = root => {
    if(!root) return true;
    let stack = [];
    let isValidBSTFlag = true;
    let max = -Number.MAX_VALUE;
    while (1) {
        while(root != null){
            stack.push(root);
            root = root.left;
        }
        if (stack.length === 0) break;
        let node = stack.pop();
        if (node.val > max) {
            max = node.val;
        } else {
            isValidBSTFlag = false;
            break;
        }
        root = node.right;
    }
    return isValidBSTFlag;
}

三、二叉搜刮树的插进去

相干问题:leetcode 701.二叉搜刮树中的插进去操纵 [中等]

将值插进去二叉搜刮树,只需树在插进去后仍坚持为二叉搜刮树即可。

思绪:找到大于插进去节点值的节点,将要插进去的节点作为该节点的左子树。注重细节。

这里照样用中序遍历,中序遍历能很好地处理一种状况,就是要插进去的节点值比树中的一切节点还大。

这类状况,找到树中最大值的节点,将插进去的节点作为该节点的右节点。

没用递归,轻易明白。

const insertIntoBST = (root, val) => {
    let stack = [];
    let r = root;
    let node = null;
    while (1) {
        while(root != null) {
            stack.push(root);
            root = root.left;
        }
        if (stack.length === 0) break;
        node = stack.pop();
        // 找到大于插进去节点值的节点
        if (node.val > val) {
            let newNode = new TreeNode(val);
            newNode.left = node.left;
            // 这里是细节
            node.left = newNode;
            break;
        }
        root = node.right;
    }
    // 要插进去的节点值比树中的一切节点还大
    if (val > node.val) {
        node.right = new TreeNode(val);
    }
    return r;
};

四、二叉搜刮树的恢复

相干问题:leetcode 99.恢复二叉搜刮树 [难题]

请求:二叉搜刮树中的两个节点被毛病地交流。请在不转变其构造的状况下,恢复这棵树。

思绪:应用中序遍历找到毛病的两个节点s1,s2。交流这两个节点。

用一个数组保留遍历的值,假如前一个节点大于后一个节点,则s1肯定是前一个节点,后一个节点不一定是s2,继承遍历寻觅找到s2。

const recoverTree = root => {
    let res = [];
    let s1 = s2 = null;
    const orderSearch = root => {
        if (root) {
            orderSearch(root.left);
            if (res.length !== 0) {
                if (res[res.length - 1].val > root.val) {
                    // 第一个找到的才是s1
                    !s1 && (s1 = res[res.length - 1]);
                    // 如有第二次,第二次的才是s2
                    s2 = root;
                }
            }
            
            res.push(root)
            orderSearch(root.right);
        }
    }
    orderSearch(root);
    [s1.val, s2.val] = [s2.val, s1.val];
    return root;
};

总结:

二叉搜刮树跟排序相干,老是围绕着中序遍历举行操纵。

递归代码简约然则不好明白,非递归相对轻易明白一些,二者效力差不太大,看场景运用。

    原文作者:陈小俊
    原文地址: https://segmentfault.com/a/1190000017148291
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞