二叉搜索树专题

1.验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。
输入:
    2
   / \
  1   3
输出: true

链接:https://leetcode-cn.com/problems/validate-binary-search-tree/
(1)中序遍历解法
中序遍历二叉搜索树是递增序列,每次遍历取出前一个值,若当前值不大于前一个值则不是二叉搜索树。

var isValidBST = function (root) {
    let stack = [], p = root, pre = null;
    while (p || stack.length) {
        while (p) {
            stack.push(p);
            p = p.left;
        }
        let node = stack.pop();
        /* pre还未赋值或者小于node.val则符合条件,继续遍历 */
        if (pre === null || pre < node.val) pre = node.val;
        else return false;
        p = node.right;
    }
    return true;
};

(2)结点上下限判断解法
自上而下给结点设置上下界限,不符合则返回false

/* ------------递归------------ */
var isValidBST = function (root) {
    function check(node, x, y) {
        if (!node) return true;
        /* 当前结点值若在范围以外直接返回false */
        if (node.val <= x || node.val >= y) return false;
        return check(node.left, x, node.val) && check(node.right, node.val, y);
    }
    return check(root, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
};
/* ------------非递归------------ */
var isValidBST = function (root) {
    if (!root) return true;
    let queue = [root];
    root.x = Number.MIN_SAFE_INTEGER;
    root.y = Number.MAX_SAFE_INTEGER;
    while (queue.length) {
        let node = queue.shift();
        if (node.val <= node.x || node.val >= node.y)
            return false;
        if (node.left) {
            node.left.x = node.x;
            node.left.y = node.val;
            queue.push(node.left);
        }
        if (node.right) {
            node.right.x = node.val;
            node.right.y = node.y;
            queue.push(node.right);
        }
    }
    return true;
}

2.将有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
      0
     / \
   -3   9
   /   /
 -10  5

链接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/
自上而下,取限定区域内的中间值作为当前结点值即可

/* ----------------递归---------------- */
var sortedArrayToBST = function (nums) {
    function build(x, y) {
        if (x > y) return null;
        if (x == y) return new TreeNode(nums[x]);
        /* 获取中间值赋给当前结点 */
        let m = Math.floor(x + y >> 1);
        let node = new TreeNode(nums[m]);
        node.left = build(x, m - 1);/* 左孩子 */
        node.right = build(m + 1, y);/* 右孩子 */
        return node;
    }
    return build(0, nums.length - 1);
};
/* ----------------非递归---------------- */
var sortedArrayToBST = function (nums) {
    if (!nums.length) return null;
    let index = Math.floor(nums.length - 1 >> 1);
    let root = new TreeNode(nums[index]);
    root.x = 0; root.y = nums.length - 1;
    root.index = index;
    /* queue保存未访问的结点 */
    let queue = [root];
    while (queue.length) {
        let node = queue.shift();
        /* 判断左结点边界[node.x,node.index-1] */
        if (node.x <= node.index - 1) {
            let index = Math.floor(node.x + node.index - 1 >> 1);
            node.left = new TreeNode(nums[index]);
            /* 边界 */
            node.left.index = index;
            node.left.x = node.x;
            node.left.y = node.index - 1;
            queue.push(node.left);
        }
        /* 判断右结点边界[node.index + 1,node.y] */
        if (node.index + 1 <= node.y) {
            let index = Math.floor(node.index + 1 + node.y >> 1);
            node.right = new TreeNode(nums[index]);
            /* 边界 */
            node.right.index = index;
            node.right.x = node.index + 1;
            node.right.y = node.y;
            queue.push(node.right);
        }
    }
    return root;
};

3.二叉树展开为链表

给定一个二叉树,原地将它展开为链表。

例如,给定二叉树
    1
   / \
  2   5
 / \   \
3   4   6
将其展开为:
1
 \
  2
   \
    3
     \
      4
       \
        5
         \
          6

链接:https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/
后序遍历,遍历每个结点node时(其左子树已经处理成为链表)找到左子树链表尾部结点tmp,将node的右子树接到tmp右边,然后将node左子树放到右边,左边置空.

    1                  1                 1
   / \                / \               / \
  2   5     -->      2   5     -->     2   5
 / \   \            / \   \             \   \
3   4   6          3 - 4   6             3   6
1:tmp.right=right.right;                  \
2:node.right = node.left;node.left=null;   4
var flatten = function (root) {
    let stack = [], p = root, vis = new Set();
    while (stack.length || p) {
        while (p) {
            stack.push(p);
            p = p.left;
        }
        let node = stack[stack.length - 1];
        /* 若右子树存在且未访问,先遍历右子树 */
        if (node.right && !vis.has(node.right)) {
            vis.add(node.right);
            p = node.right;
        } else {
            if (node.left) {
                /*  找链表的尾部 */
                let tmp = node.left;
                while (tmp.right) tmp = tmp.right;
                /* 将node右子树链接到链表尾部 */
                tmp.right = node.right;
                /* 将node右边接左子树,左子树清空 */
                node.right = node.left;
                node.left = null;
            }
            stack.pop();
        }
    }
    return root;
};

4.不同的二叉搜索树 II

给定一个整数 n,生成所有由 1 … n 为节点所组成的二叉搜索树。

输入: 3
输出:
[
  [1,null,3,2],
  [3,2,null,1],
  [3,1,null,null,2],
  [2,1,3],
  [1,null,2,null,3]
]
解释:
以上的输出对应以下 5 种不同结构的二叉搜索树:
   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

链接:https://leetcode-cn.com/problems/unique-binary-search-trees-ii/
每个数作为根结点的值来连接不同的左子树和右子树,将这些组合存入数组返回作为其他值的左子树或右子树进行同样的操作。

/* ----------------递归---------------- */
var generateTrees = function (n) {
    if (!n) return [];
    function getTrees(x, y) {
        if (x > y) return [null];
        if (x == y) return [new TreeNode(x)];
        let res = [];
        for (let i = x; i <= y; i++) {
            /* 获得左,右子树数组 */
            let leftTrees = getTrees(x, i - 1);
            let rightTrees = getTrees(i + 1, y);
            /* 左,右子树数组合并 */
            for (let leftTree of leftTrees) {
                for (let rightTree of rightTrees) {
                    let root = new TreeNode(i);
                    root.left = leftTree;
                    root.right = rightTree;
                    res.push(root);
                }
            }
        }
        return res;
    }
    return getTrees(1, n);
};
/* ----------------非递归---------------- */
var generateTrees = function (n) {
    if (!n) return [];
    /* 结构不变,值增加offset */
    function offsetTree(node, offset) {
        if (!node) return null;
        let newNode = new TreeNode(node.val + offset);
        newNode.left = offsetTree(node.left, offset);
        newNode.right = offsetTree(node.right, offset);
        return newNode;
    }
    let trees = [];
    trees[0] = [null];
    for (let i = 1; i <= n; i++) {
        trees[i] = [];/* 存储结点数为i个的二叉树 */
        for (let j = 1; j <= i; j++) {/* 根节点值为j */
            let leftNum = j - 1, rightNum = i - j;/* 左右子树的结点个数 */
            for (let leftTree of trees[leftNum]) {
                for (let rightTree of trees[rightNum]) {
                    /* 创建根节点 */
                    let root = new TreeNode(j);
                    root.left = leftTree;/* [1,j-1] */
                    root.right = offsetTree(rightTree, j);/* [1,i-j]值偏移j=>[j+1,i] */
                    trees[i].push(root);
                }
            }
        }
    }
    return trees[n];
};
posted @ 2020-04-19 14:19  aeipyuan  阅读(136)  评论(0编辑  收藏  举报