二叉搜索樹專題

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];
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章