Skip to content

二叉树部分操作

字数
1566 字
阅读时间
8 分钟

因为二叉树的数据结构特性天然支持递归,所以二叉树的一般操作都存在迭代和递归两种实现方式。

翻转二叉树

226. 翻转二叉树

思路:本质上是遍历二叉树,在“读入”节点时修改其左右子节点的指向,从而实现翻转。

迭代法

javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var invertTree = function(root) {
    if (root === null) {
        return root;
    }
    const stack = [root];
    while (stack.length) {
        const node = stack.pop();
        // 交换左右子节点
        let temp = node.left;
        node.left = node.right;
        node.right = temp;

        // 将子节点入栈,以便后续处理
        if (node.right) {
            stack.push(node.right);
        }
        if (node.left) {
            stack.push(node.left);
        }
    }
    return root;
};

递归法

javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var invertTree = function(root) {
    if (root === null) {
        return root;
    }
    // 递归翻转左右子树
    invertTree(root.left);
    invertTree(root.right);

    // 交换当前节点的左右子节点
    let temp = root.left;
    root.left = root.right;
    root.right = temp;

    return root;
};

对称二叉树

101. 对称二叉树 - 力扣(LeetCode)

思路:对称二叉树可以看作镜像二叉树。判断镜像的巧妙方式是比较左右子树是否对称,即比较 左子树的左节点右子树的右节点,以及 左子树的右节点右子树的左节点

迭代法

javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function(root) {
    if (root === null) return true;
    let queue = [];
    queue.push(root.left);
    queue.push(root.right);
    while (queue.length) {
        let leftNode = queue.shift();
        let rightNode = queue.shift();
        // 左右节点都为空,则对称,继续检查队列中的其他节点
        if (leftNode === null && rightNode === null) continue;
        // 左右节点有一个为空,或者值不相等,则不对称
        if (leftNode === null || rightNode === null || leftNode.val !== rightNode.val) return false;
        // 注意入队顺序:左子树的左孩子与右子树的右孩子,左子树的右孩子与右子树的左孩子
        queue.push(leftNode.left);
        queue.push(rightNode.right);
        queue.push(leftNode.right);
        queue.push(rightNode.left);
    }
    return true;
};

递归法

javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function(root) {
    if (root === null) return true; // 空树是对称的
    
    const compareTwo = function(left, right) {
        // 左右节点都为空,则对称
        if (left === null && right === null) return true;
        // 左右节点有一个为空,或者值不相等,则不对称
        if (left === null || right === null || left.val !== right.val) return false;
        
        // 递归比较外侧节点和内侧节点
        let outSide = compareTwo(left.left, right.right);
        let inSide = compareTwo(left.right, right.left);
        return outSide && inSide;
    };

    return compareTwo(root.left, root.right);
};

二叉树的最大深度

104. 二叉树的最大深度 - 力扣(LeetCode)

思路:可以使用层序遍历(广度优先遍历)来计算二叉树的最大深度。每遍历一层,深度加一。

javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxDepth = function(root) {
    let depth = 0;
    if (root === null) {
        return depth;
    }
    const queue = [root];
    while (queue.length) {
        let size = queue.length; // 当前层的节点数量
        depth++; // 深度加一
        for (let i = 0; i < size; i++) { // 遍历当前层的所有节点
            let node = queue.shift();
            if (node.left) {
                queue.push(node.left);
            }
            if (node.right) {
                queue.push(node.right);
            }
        }
    }
    return depth;
};

二叉树的最小深度

111. 二叉树的最小深度 - 力扣(LeetCode)

思路:与最大深度类似,但需要注意的是,当一个节点只有一个子节点时,最小深度不是 1,而是 1 加上其存在的子树的最小深度。只有当左右子节点都为空时,才算到达叶子节点。

javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var minDepth = function(root) {
    const getMinDepth = function(node) {
        if (node === null) return 0;
        
        let leftDepth = getMinDepth(node.left);
        let rightDepth = getMinDepth(node.right);

        // 如果左子树为空,右子树不为空,则最小深度为 1 + 右子树的最小深度
        if (node.left === null && node.right !== null) {
            return 1 + rightDepth;
        }
        // 如果右子树为空,左子树不为空,则最小深度为 1 + 左子树的最小深度
        if (node.left !== null && node.right === null) {
            return 1 + leftDepth;
        }
        // 左右子树都不为空,则最小深度为 1 + 左右子树中较小的深度
        const depth = 1 + Math.min(leftDepth, rightDepth);
        return depth;
    };
    return getMinDepth(root);
};

完全二叉树的节点个数

222. 完全二叉树的节点个数 - 力扣(LeetCode)

思路:完全二叉树有两种情况:

  1. 满二叉树:可以直接用 2^树深度 - 1 来计算(根节点深度为 1)。
  2. 最后一层叶子节点未满:分别递归计算左子树和右子树的节点个数。在递归过程中,如果发现某个子树是满二叉树,则按情况 1 计算。
javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var countNodes = function(root) {
    if (root === null) return 0;

    let left = root.left;
    let right = root.right;
    let leftHeight = 0;
    let rightHeight = 0;

    // 计算左子树的高度
    while (left) {
        leftHeight++;
        left = left.left;
    }
    // 计算右子树的高度
    while (right) {
        rightHeight++;
        right = right.right;
    }

    // 如果左右子树高度相等,说明以当前节点为根的树是满二叉树
    if (leftHeight === rightHeight) {
        return (2 << leftHeight) - 1; // 2^height - 1
    }
    // 否则,递归计算左右子树的节点数,并加上根节点本身
    return countNodes(root.left) + countNodes(root.right) + 1;
};


<NolebaseGitContributors />


<NolebaseGitChangelog />

撰写