[路飞]_前端算法第五十八弹- 二叉树最大宽度

158 阅读2分钟

给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与**满二叉树(full binary tree)**结构相同,但一些节点为空。

每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。

示例 1:

输入:
       1
     /   \\
    3     2
   / \\     \\
  5   3     9
**输出**: 4
**解释:** 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。

示例 2:

 输入: 

          1
         /  
        3    
       / \\       
      5   3     

输出: 2
解释: 最大值出现在树的第 3 层,宽度为 2 (5,3)。

示例 3:

输入: 

          1
         / \\
        3   2 
       /        
      5      

输出: 2
解释: 最大值出现在树的第 2 层,宽度为 2 (3,2)。

示例 4:

输入: 

          1
         / \\
        3   2
       /     \\  
      5       9 
     /         \\
    6           7
输出: 8
解释: 最大值出现在树的第 4 层,宽度为 8 (6,null,null,null,null,null,null,7)。

注意: 答案在32位有符号整数的表示范围内。

BFS广度优先遍历

广度优先是先遍历每一层的所有结点,记录宽度,再向下一层遍历。根据满二叉树的性质,假定当前节点在数组中的下标为i,那么其左子节点的下标为2i,其右子结点的下标为2i+1,所以,记录当前层的最左节点的下标,再用当前节点的下标想减再+1,便可得到当前层的宽度。然后与记录值ans进行比较,再进行下层遍历。

var widthOfBinaryTree = function (root) {
    let q = [];
    let ans = 0
    q.push([root, 0]);
    while (q.length > 0) {
        let len = q.length;
        let l = q[0][1], r = q[0][1];
        for (let i = 0; i < len; i++) {
            item = q[0];
            r = item[1]
            if (item[0].left) {
                q.push([item[0].left, r * 2])
            }
            if (item[0].right) {
                q.push([item[0].right, r * 2 + 1])
            }
            q.shift()
        }
        ans = Math.max(ans, r - l + 1)
    }
    return ans
};

但是这种情况下,当数据过大的时候会出现栈溢出,导致最后的结果出现NAN,所以我们需要进行数据优化,我们将 q.push([item[0].left, r * 2])中的r改为(r-l),通过改变下标数的量级,防止溢栈。

var widthOfBinaryTree = function (root) {
    let q = [];
    let ans = 0
    q.push([root, 0]);
    while (q.length > 0) {
        let len = q.length;
        let l = q[0][1], r = q[0][1];
        for (let i = 0; i < len; i++) {
            item = q[0];
            r = item[1]
            if (item[0].left) {
                q.push([item[0].left, (r - l) * 2])
            }
            if (item[0].right) {
                q.push([item[0].right, (r - l) * 2 + 1])
            }
            q.shift()
        }
        ans = Math.max(ans, r - l + 1)
    }
    return ans
};

DFS深度优先

深度优先则是每次都将该节点的所有子节点遍历完成,记录每个节点的深度,并记录当前深度的结点值,如果当前节点没有被记录过,则记录当前深度的最左节点下标,如果当前深度记录过,则用当前节点的下标与当前节点的首下标进行想减,取当前深度的节点数。

var widthOfBinaryTree = function (root) {
    let ans = 0;
    let left = new Map();
    dfs(root, 0, 0);
    return ans;
    function dfs(root, depth, pos) {
        if (!root) return;
        if (!left.has(depth)) left.set(depth, pos);
        ans = Math.max(ans, pos - left.get(depth) + 1);
        dfs(root.left, depth + 1, 2 * (pos - left.get(depth)));
        dfs(root.right, depth + 1, (pos - left.get(depth)) * 2 + 1)
    }
};