【算法17天:Day17】第六章二叉树 LeetCode 平衡二叉树(110)

107 阅读4分钟

题目一:

image.png

解法一:(递归)

解题思路:

image.png

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数。
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数。
  • Leetcode 以节点数为主

求深度可以从上到下去查,所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)

递归三部曲:

  1. 明确递归函数的参数

参数:当前传入结点

那么如何标记左右子树是否差值大于1呢?

如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。

所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。

代码如下:

// -1 表示已经不是平衡二叉树了,否则返回值是以该节点为根节点树的高度
var getHeight = function(node) {}
  1. 明确终止条件

递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0

代码如下:

if (node == NULL) {
    return 0;
}
  1. 明确单层递归的逻辑

如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。

分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则则返回-1,表示已经不是二叉平衡树了。

var isBalanced = function(root) {
    var getHeight = function(cur) {
        if (cur === null) return 0
        let leftHeight = getHeight(cur.left)
        if (leftHeight == -1) return -1
        let rightHeight = getHeight(cur.right)
        if (rightHeight == -1) return -1
        if (Math.abs(leftHeight - rightHeight) > 1) {
            return -1
        } else {
            return 1 + Math.max(leftHeight, rightHeight)
        }

    }
    let isBalanced = getHeight(root)
    return isBalanced == -1 ? false : true
};
// 或者
var isBalanced = function(root) {
    //还是用递归三部曲 + 后序遍历 左右中 当前左子树右子树高度相差大于1就返回-1
    // 1. 确定递归函数参数以及返回值
    const getDepth = function(node) {
        // 2. 确定递归函数终止条件
        if(node === null) return 0;
        // 3. 确定单层递归逻辑
        let leftDepth = getDepth(node.left); //左子树高度
        // 当判定左子树不为平衡二叉树时,即可直接返回-1
        if(leftDepth === -1) return -1;
        let rightDepth = getDepth(node.right); //右子树高度
        // 当判定右子树不为平衡二叉树时,即可直接返回-1
        if(rightDepth === -1) return -1;
        if(Math.abs(leftDepth - rightDepth) > 1) {
            return -1;
        } else {
            return 1 + Math.max(leftDepth, rightDepth);
        }
    }
    return !(getDepth(root) === -1);
};

解法二:(迭代)

解题思路:在104.二叉树的最大深度 (opens new window)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。因为总体上深度是从上到下(层次遍历),高度是从下到上(后序遍历)。

本题的迭代方式可以先定义一个函数,专门用来求高度。

这个函数通过栈模拟的后序遍历找每一个节点的高度(其实是通过求传入节点为根节点的最大深度来求的高度

然后再用栈来模拟后序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合。

当然此题用迭代法,其实效率很低,因为没有很好的模拟回溯的过程,所以迭代法有很多重复的计算。

总结:通过本题可以了解求二叉树深度 和 二叉树高度的差异,求深度适合用前序遍历,而求高度适合用后序遍历。

虽然理论上所有的递归都可以用迭代来实现,但是有的场景难度可能比较大。

// 获取当前节点的高度
var getHeight = function (curNode) {
    let stack = [];
    if (curNode !== null) stack.push(curNode); // 压入当前元素
    let depth = 0, res = 0;
    while (stack.length) {
        let node = stack[stack.length - 1]; // 取出栈顶
        if (node !== null) {
            stack.pop();
            stack.push(node);   // 中
            stack.push(null);
            depth++;
            node.right && stack.push(node.right);   // 右
            node.left && stack.push(node.left);     // 左
        } else {
            stack.pop();
            node = stack[stack.length - 1];
            stack.pop();
            depth--;
        }
        res = res > depth ? res : depth;
    }
    return res;
}
var isBalanced = function (root) {
    if (root === null) return true;
    let stack = [root];
    while (stack.length) {
        let node = stack[stack.length - 1]; // 取出栈顶
        stack.pop();    
        if (Math.abs(getHeight(node.left) - getHeight(node.right)) > 1) {
            return false;
        }
        node.right && stack.push(node.right);
        node.left && stack.push(node.left);
    }
    return true;
};