2
头图

Contact us : Youdao technical team assistant : ydtech01 / mailbox : ydtech@rd.netease.com

Foreword

This article belongs to the second part of a series of articles "Do you really understand binary trees"-the shredded algorithm article.

If you have not read the first part "Do you really understand binary trees (Basics of tree structure)" , it is strongly recommended to read the first part first, so that you will be more powerful in solving problems. Many of the contents already mentioned in the first article will not be repeated here.

One and binary tree basic brushing part

traversal of binary tree 16126f5afefe18

Problem-solving ideas

If you have read my last article "Do you really understand binary trees (basic tree structure)" , you should already know that the traversal of our tree is inherently suitable for using recursively implement .

In addition, I also talked about how to design and implement a recursive function. If you are not familiar with two points or have not read the previous article, please move to "Do you really understand binary trees (Basics of tree structure) )" Make up the basics first, I won’t go into details here, just talk about the ideas directly:

First, let’s design our recursive function

  1. Function meaning: preorder traverses the binary tree with root as the root node
  2. Boundary conditions: no need to traverse when root is empty, return to root directly
  3. Recursive process: traverse the left subtree in preorder and the right subtree in preorder respectively

code demo

/*
 * @lc app=leetcode.cn id=144 lang=typescript
 *
 * [144] 二叉树的前序遍历
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function preorderTraversal(root: TreeNode | null, arr: number[]=[]): number[] {
    // 判断边界条件
    if(root) {
        // 因为是前序遍历,所以先将根节点加入数组
        arr.push(root.val);
        // 递归遍历左子树和右子树
        preorderTraversal(root.left, arr);
        preorderTraversal(root.right, arr);
    }
    return arr;
};
// @lc code=end

The above is a conventional recursive implementation, we can also use iterative method achieve:


/**
 * 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 preorderTraversal = function(root) {
      // 用一个栈辅助完成迭代遍历
    const stack = [];
      // 结果数组
    const res = [];
      // 当前节点
    let cur = root;
        // 如果当前节点为空或者栈为空时结束循环
    while(cur || stack.length>0) {
        while(cur) {
            // 因为是前序遍历,所以根节点先存放到结果数组
            res.push(cur.val);
              // 为了在我们走到左子树的底部时,能够回到根节点继续遍历右子树,因此,先将根节点存入栈中
            stack.push(cur);
              // 继续遍历左子树,直到遍历到叶子节点
            cur = cur.left;
        }
          // 左子树遍历完了,我们需要遍历右子树了,首先我们先要从栈顶把最近的一个根节点弹出
        cur = stack.pop();
          // 遍历弹出的这个根节点的右子树
        cur = cur.right;
    }
    return res;
};

1.2 LeetCode 589 Preorder traversal of N-ary tree

problem-solving ideas

The starting point of the problem-solving idea is exactly the same as that of the binary tree, so I won’t repeat it here.

First, let’s design our recursive function

  1. Function meaning: Preorder traverses the N-ary tree with root as the root node
  2. Boundary conditions: no need to traverse when root is empty, return the result array directly
  3. Recursive process: Recursively traverse all subtrees under root

Code demo

/*
 * @lc app=leetcode.cn id=589 lang=typescript
 *
 * [589] N 叉树的前序遍历
 */

// @lc code=start
/**
 * Definition for node.
 * class Node {
 *     val: number
 *     children: Node[]
 *     constructor(val?: number) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.children = []
 *     }
 * }
 */

function preorder(root: Node | null, arr: number[] = []): number[] {
    // 边界条件
    if(!root) return arr;
    // 前序遍历,先将根节点放入结果数组
    arr.push(root.val);
    // 循环递归调用所有的子节点
    root.children.forEach(child => {
        preorder(child, arr);
    });
    return arr;
};
// @lc code=end

1.3 LeetCode 226 Flip Binary Tree

Problem solving ideas

If we want to reverse a binary tree, we must first reverse the subtree of the left subtree and the subtree of the right subtree of the binary tree, and then reverse the left and right subtrees of the root node.

Here, the new feature of deconstruction assignment in js is used for efficient and fast reverse.

First, let’s design our recursive function

  1. Function meaning: Reverse the binary tree with root as the root node
  2. Boundary conditions: no need to reverse when root is empty, return to root directly
  3. Recursive process: Reverse the left and right subtrees of root respectively

code demo

/*
 * @lc app=leetcode.cn id=226 lang=typescript
 *
 * [226] 翻转二叉树
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function invertTree(root: TreeNode | null): TreeNode | null {
    // 边界条件,当root为空时直接返回
    if(!root) return root;
    // 先交换root的左右子树,这里我们利用js的解构赋值的新特性可以迅速的交换左右子树
    [root.left, root.right] = [root.right, root.left];
    // 分别递归左右子树
    invertTree(root.left)
    invertTree(root.right)
    return root;
};
// @lc code=end

1.4 LeetCode Sword refers to Offer32. Print binary tree from top to

Problem-solving ideas

This question is actually traversing the hierarchy of our binary tree, outputting the nodes of each layer in turn. A technique can be used here, adding a variable k to represent the current level of the binary tree traversed, the default is 0, that is, the default is the first level of the binary tree.

First, let’s design our recursive function

  1. Function meaning: add the left subtree and right subtree with root as the end of the root node to the array where the current layer k is located
  2. Boundary conditions: no need to traverse when root is empty, return directly, return the result array directly
  3. Recursive process: Recursively traverse the left and right subtrees of root, don’t forget that the number of layers needs to be increased by 1.

code demo

/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function levelOrder(root: TreeNode | null,k=0, res: number[][] = []): number[][] {
    // 边界条件,当root不存在时,直接返回结果数组
    if(!root) return res;
    // 如果结果数组的长度为k,说明我们准备遍历新的一层,但这层还没有数组来存放数据,所以要新建一个数组放到结果数组末尾
    if(res.length===k) res.push([]);
    // 将根节点的值放到第k位的数组中
    res[k].push(root.val);
    // 递归遍历左子树、右子树
    levelOrder(root.left, k+1, res);
    levelOrder(root.right, k+1, res);
    return res;
};

The implementation of our binary tree hierarchy traversal can not only use recursion, queue + iteration , and the ideas are similar.

/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */
function levelOrder(root: TreeNode | null, res: number[][]=[]): number[][] {
    // 边界条件,root不存在是直接返回空数组
    if(!root) return res;
    // 用一个队列来存储节点,初始时根节点入队
    const queue: TreeNode[] = [root];
    // 用于记录当前遍历到了第几层
    let k = 0;
    while(queue.length!==0) {
        // 当层数与结果数组长度一致时,说明遍历到了下一层,但没有一个数组用来存放下一层的数据,因此在结果数组最后放一个空数组
        if(k===res.length) res.push([]);
        // 将队列中的所有节点依次弹出,此时,队列中的所有节点都是当前层的节点,并且是从左到右排列的
        let queueLen = queue.length;
        // 注意,我们这行的终止条件是针对于本次循环的队列长度,如果在循环过程中动态加入队列中的元素,是不会循环到的,因此,可以在循环过程中
        // 不断的将有子节点的元素的子节点压入队列而不用担心把下一层的数据也在本行输出
        while(queueLen--) {
            // 当根节点出队并将结果压入到第k层的数组中
            const item = queue.shift();
            res[k].push(item.val);
            // 如果左子树存在则左子树入队
            item.left && queue.push(item.left);
            // 右子树存在,则右子树入队
            item.right && queue.push(item.right);
        }
        // 本层所有节点处理完后,记得层数加一,准备遍历下一层
        k++;
    }
    return res;
};

1.5 LeetCode 107 Binary Tree Sequence TraversalⅡ

Problem-solving ideas

The beginning of this question is no different from the previous one. It just reverses the result array of the previous question. This is extremely simple to implement in js, so I won't repeat it.

code demo

/*
 * @lc app=leetcode.cn id=107 lang=typescript
 *
 * [107] 二叉树的层序遍历 II
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function _lavelOrder(root: TreeNode|null, k: number, arr: number[][] = []): number[][] {
    if(!root) return arr;
    if(arr.length===k) arr.push([]);
    arr[k].push(root.val);
    _lavelOrder(root.left, k+1, arr);
    _lavelOrder(root.right, k+1, arr);
    return arr;
}

function levelOrderBottom(root: TreeNode | null): number[][] {
    const res = _lavelOrder(root, 0);
    // 使用双指针方式交换反转数组
    for(let i=0,j=res.length-1;i<j;i++,j--) {
        [res[i], res[j]] = [res[j], res[i]];
    }
    return res;
        // 也可以直接使用数组自带的反转方法
    // return res.reverse();
};
// @lc code=end

1.6 LeetCode 103 Zigzag sequence traversal of binary tree

Problem-solving ideas

This question is actually a synthesis of the above two questions. We only need to traverse the output in the normal layer order to get the result array, and then flip the odd rows in the result array.

Of course, we can use a better method, that is, when traversing the layer sequence, directly determine whether the current layer number k is odd or even, if it is odd, add elements to the front of the array, otherwise add elements to the back of the array, so that it won’t You need to invert the array again.

code demo

plan one : first traverse the normal layer sequence, and then invert the odd-numbered row array

function _zigzagLevelOrder(root: TreeNode | null, k:number=0, res: number[][]=[]): number[][] {
    if(!root) return res;
    if(k===res.length) res.push([]);
    res[k].push(root.val);
    _zigzagLevelOrder(root.left, k + 1, res);
    _zigzagLevelOrder(root.right, k + 1, res);
    return res;
};

function zigzagLevelOrder(root: TreeNode | null): number[][] {
    const res = _zigzagLevelOrder(root);
    // 将奇数行反转
    return res.map((item, index) => {
        if(index&1) {
            item = item.reverse();
        }
        return item;
    })
}

plan two : when performing layer sequence traversal, if k is an odd number, add elements from the front of the array, otherwise add elements from the back

function _zigzagLevelOrder(root: TreeNode | null, k:number=0, res: number[][]=[]): number[][] {
    if(!root) return res;
    if(k===res.length) res.push([]);
    (k&1)?res[k].unshift(root.val):res[k].push(root.val);
    _zigzagLevelOrder(root.left, k + 1, res);
    _zigzagLevelOrder(root.right, k + 1, res);
    return res;
};

function zigzagLevelOrder(root: TreeNode | null): number[][] {
    return _zigzagLevelOrder(root);
}

Two, binary tree advanced brushing part

2.1 LeetCode 110 Balanced Binary Tree

Problem solving ideas

Concept: For any subtree, the tree whose height difference between the left and right subtrees does not exceed 1 is called a balanced binary tree.

So, according to the meaning of the question, what we can think of is to calculate the height of the left and right subtrees of a tree, and then see if the absolute value of the difference between them is greater than 1, in order to judge whether the tree is a balanced tree using only one recursion. When calculating the height of the tree, suppose that if the tree is not a balanced tree, that is to say, when the absolute value of the height difference between the left and right subtrees is greater than 1, we directly return a negative number, such as -1, so that when After our program is executed, we can know whether the current tree is a balanced tree by tree is less than 0

First, let’s design our recursive function

  1. Function meaning: While calculating the height of the tree whose root is the root node, if the tree is not balanced, a negative number will be returned
  2. Boundary condition: when root is empty, the height of the tree is 0
  3. Recursive process: Calculate the tree heights of the left and right subtrees separately, and the tree height of the entire tree is the maximum height of the left and right subtrees plus one

code demo

/*
 * @lc app=leetcode.cn id=110 lang=typescript
 *
 * [110] 平衡二叉树
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */
function getHeight(root: TreeNode | null): number {
    // 边界条件,如果根节点为空,则树高0
    if(!root) return 0;
    // 分别计算左右子树的树高
    const lH = getHeight(root.left);
    const rH = getHeight(root.right);
    // 假设:如果当前root的左右子树不平衡,则返回一个负数,如-1
      // 除此之外,因为是递归程序,如果左右子树的子树的高小于0,那就说明他们的子树已经不平衡了,无需继续递归,直接返回-1
    if(Math.abs(lH-rH)>1 || lH < 0 || rH < 0) return -1;
    // 一个树的树高,应该是左右子树的树高最大值加一
    return Math.max(lH, rH) + 1;

}
function isBalanced(root: TreeNode | null): boolean {
    // 只要树高大于等于0,说明这颗树是平衡的
    return getHeight(root) >= 0;
};
// @lc code=end

2.2 LeetCode 112 Total path

Problem solving ideas

The idea of solving this problem is actually that when we keep looking down, we calculate a difference between targetNum and the value of the current node as the targetNum for the next recursion, so that when we find the leaf node , As long as the value of the leaf node end is equal to targetNum, it means that this path exists.

First, let’s design our recursive function

  1. Function meaning: whether the sum of nodes can be found in a binary tree with root as the root node, which is equal to the value of targetNum
  2. Boundary condition: When root is empty, it cannot exist, return false. In addition, if the current node is a leaf node (nodes without left and right subtrees), then the value of this node must be equal to targetNum
  3. Recursive process: Recursively judge whether the left subtree and the right subtree meet the conditions. It should be noted that the targetNum that we pass into the recursive function needs to be interpolated with the current node and then passed on.

code demo

/*
 * @lc app=leetcode.cn id=112 lang=typescript
 *
 * [112] 路径总和
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function hasPathSum(root: TreeNode | null, targetSum: number): boolean {
    // 边界条件,如果root为空时,直接返回false
    if(!root) return false;
    // 如果当前节点是叶子节点,那么这个节点的值必须等于targetSum
    if(!root.left && !root.right) return root.val === targetSum;
    // 在左子树中查找是否有满足条件的路径
    if(root.left && hasPathSum(root.left, targetSum - root.val)) return true; 
    // 在右子树中查找是否有满足条件的路径
    if(root.right && hasPathSum(root.right, targetSum - root.val)) return true;
    // 都不满足则返回false
    return false;
};
// @lc code=end

2.3 LeetCode 105 Construct a binary tree from pre-order and middle-order traversal sequence

Problem-solving ideas

If you have read my previous article "Do you really understand binary trees (Basics of tree structure)" students should not be unfamiliar. In the last article, we have already analyzed the idea of solving this problem at the level of thinking and logic, but we have not coded it together. Now it is time to pay off the debt.

Let’s review the general idea again:

  1. First find the location of the root node
  2. Recursively build the left subtree
  3. Recursively build the right subtree
  4. Then hang the left and right subtrees on the root node
# 前序遍历输出结果
<1> 5 2 3 4
# 中序遍历输出结果
5 <1> 3 2 4
# 我们知道前序遍历的第一位就是我们二叉树的根节点,那么,我们根据这个根节点的数值在中序遍历中找到1所在的位置
# 这样,我们就知道,5是左子树,3 2 4是1的右子树序列,左子树无需继续处理,右子树可以直接递归处理得到最终的结果

code demo

/*
 * @lc app=leetcode.cn id=105 lang=typescript
 *
 * [105] 从前序与中序遍历序列构造二叉树
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function buildTree(preorder: number[], inorder: number[]): TreeNode | null {
    if(preorder.length===0) return null;
      // 用于记录根节点在中序遍历中的位置
    let pos = 0;
    // 找到根节点在中序遍历序列中的位置
    while(inorder[pos] !== preorder[0]) ++pos;
        // 分别用来存储拆分后的左右子树的前序和中序遍历序列
    let l_pre = [], l_in = [], r_pre=[],r_in=[];

    // 将左子树的前序遍历序列和中序遍历序列存起来
    for(let i=0;i<pos;i++) {
        l_pre.push(preorder[i+1]);
        l_in.push(inorder[i]);
    }
    // 将右子树的前序遍历和中序遍历的序列存起来
    for(let i=pos+1;i<preorder.length;i++) {
        r_pre.push(preorder[i]);
        r_in.push(inorder[i]);
    }
    // 构建二叉树
    const node = new TreeNode(preorder[0], buildTree(l_pre, l_in), buildTree(r_pre, r_in));
    return node;

};
// @lc code=end

2.4 LeetCode 222 Number of nodes in a complete binary tree

Problem-solving ideas

This question is to let us count how many nodes a tree has. We only need to know such a formula: number of summary points = the number of left subtree nodes + the number of right subtree nodes + the number of root nodes (1)

code demo

/*
 * @lc app=leetcode.cn id=222 lang=typescript
 *
 * [222] 完全二叉树的节点个数
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function countNodes(root: TreeNode | null): number {
    // 边界条件,如果root不存在,则返回0
    if(!root) return 0;
    return countNodes(root.left) + countNodes(root.right) + 1;
};
// @lc code=end

2.5 LeetCode refers to Offer 54 The k-

Problem solving ideas

Binary search tree: All values on the right subtree will be greater than the value of the root node, and all nodes on the left subtree will be less than the value of the root node. Therefore, the output of the middle-order traversal of the binary search tree must be an ordered array.

Since this is a binary search tree, the tree of the left subtree must be smaller than the root node, and the right subtree must be larger than the root node.
From this feature we can draw:

  1. If we are looking for the k-th largest tree, and k is less than or equal to the number of nodes in the right subtree, then we can go directly to right subtree of .
  2. If k is equal to the number of right subtree nodes plus 1, then the number we are looking for is actually root node
  3. If it is not the above two cases, then we will directly search in the left subtree, but at this time, because we have excluded the root node and the right subtree, we are no longer looking for the k-th largest node in the left subtree. we should minus the root node and the right child tree nodes number

code demo

/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */
// 计算节点数量
function getCount(root: TreeNode): number {
    if(!root) return 0;
    return getCount(root.left) + getCount(root.right) + 1;
}
// 由于这是一颗二叉搜索树,那么左子树的树肯定小于根节点,右子树肯定大于根节点
// 从这个特性我们就可以得出:
// 如果要找第k大的树,并且k小于或者等于右子树节点数量的话,那我们可以先到右子树中去查找,
// 如果k等于右子树节点数量加1,那么说明我们要找的数其实就是根节点
// 如果不是上述两种情况,那么,我们就直接在左子树中查找,不过此时,因为我们已经排除了根节点和右子树了,就不再是找左子树第k大的节点了,我们应该减去根节点和右子树节点的数量
function kthLargest(root: TreeNode | null, k: number): number {
    // 获取右子树节点数量
    const count_r = getCount(root.right);
    // 如果k小于等于右子树节点数量,则在右子树上查找第k大的树
    if(k<=count_r) return kthLargest(root.right, k);
    // 如果k等于右子树节点数量加1,说明要找的树就是根节点
    if(k===count_r+1) return root.val;
    // 否则去左子树查找第k-(count_r+1)=k- count_r - 1 大的数
    return kthLargest(root.left, k - count_r - 1);
};

2.6 LeetCode refers to the substructure of the Offer 26 tree

Problem solving ideas

This question is actually after the boundary conditions are processed, see if B can match an entire A tree. If it cannot match, then see if B can match A's left subtree or its right subtree. If If any one can be matched, it means that B is a substructure of A.

code demo

/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */
// 递归匹配子树结构
function isMatch(a: TreeNode, b: TreeNode) {
    // 如果b为空,肯定匹配,返回true
    if(!b) return true;
    // 如果a为空则不可能匹配,返回false
    if(!a) return false;
    // 如果a和b的值不一致,返回false
    if(a.val !== b.val) return false;
    // 分别递归匹配a、b的左子树和右子树
    return isMatch(a.left, b.left) && isMatch(a.right, b.right);
}

function isSubStructure(A: TreeNode | null, B: TreeNode | null): boolean {
    // 依据题意,如果B为空,则不是任何树的子结构,直接返回false
    if(!B) return false;
    // 若A为空,B不为空,则不可能匹配上,返回false
    if(!A) return false;
    // 递归判断A、B是否能够匹配,如果匹配上了,则返回true
    if(isMatch(A, B)) return true;
    // 上面的情况都不是,那么就分别去A的左子树和右子树看一下能否匹配,只要二者有一个匹配就算匹配成功
    return isSubStructure(A.left, B) || isSubStructure(A.right, B);
};

2.7 LeetCode 622 Maximum width of binary tree

Problem-solving ideas

This question is indeed difficult to implement. We can use the previous article "Do you really understand binary trees (Basics of tree structure)" The numbering method of the complete binary tree and a queue mentioned in 16126f5aff0da8 Help us to complete this topic. In the numbering of a complete binary tree, if the number of the current node is i, then the number of his left subtree is 2 i, and the number of the right subtree is 2 i+1, but in this question, it may be because of the number Too large causes the overflow of the shaped data, so we need special handling. For specific problem-solving ideas, the code comments are written very clearly, just look at the code directly.

code demo

/*
 * @lc app=leetcode.cn id=662 lang=typescript
 *
 * [662] 二叉树最大宽度
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

// 为了方便存储节点和它对应编号,定义一个包装类
class Wrapper {
    constructor(public node: TreeNode, public idx: number){

    }
}

function widthOfBinaryTree(root: TreeNode | null): number {
    // 用来存储结果
    let res = 0;
    // 定义一个包装类的队列,这个包装类可以存储节点和他对应的编号
    const queue: Wrapper[] = [];
    // 先将根节点和他的编号0加入队列
    queue.push(new Wrapper(root, 0));

    // 用于存储每一层有多少个节点
    let rowSize;
    while(rowSize = queue.length) {
        // 设置当前层最小编号和最大编号的初始值都为父节点的编号
        let lIdx = queue[0].idx, rIdx = queue[0].idx;

        // 遍历当前层的每一个元素
        while(rowSize--) {
            // 先将队首元素出队
            const { node, idx } = queue.shift();
            // 当前层最大编号为父节点的编号
            rIdx = idx;
            // 当存在左子树时,使用左子树根节点和其对应的编号创建包装类,并加入到队列
            // 其实这里,最难的就是这个编号如何界定。
            // 我们有学过完全二叉树的编号方式,可以参考这种编号方式,
            // 第i个节点的做子树根节点的编号为2*i,右子树的编号为2*i+1
            // 所以,到了这里,我们就直接使用idx * 2和idx * 2 + 1代表左子树和右子树编号
            // 如果我们这么提交的话,会遇到一个问题,就是当我们的树的节点足够大时,这个编号会超出
            // 我们整数的范围,那么,我们要怎样在不影响程序运行结果的前提下,缩减这个编号,避免整数溢出呢
            // 我们来想想,对于一个编号来说,其实最小和最大编号分别为6和7,其实跟为0和1是没有本质上的区别的
            // 因为我们拿这个编号并没有实质的作用,只是为了用这个编号计算差值,7-6的差值跟1-0的差值是一样的
            // 所以,我们这边直接让当前的编号鉴于父节点的最小编号lIdx,以此来缩减编号大小
            node.left && queue.push(new Wrapper(node.left, (idx - lIdx) * 2));
            node.right && queue.push(new Wrapper(node.right, (idx - lIdx) * 2 + 1));
        }
        // 一层循环结束后,我们只要计算上一个宽度,与当前层最大编号减去最小编号加1,然后两者取最大值找到最大宽即可
        // 例如:当前层最小和最大的编号是:0和8,那么这一层其实有8-0+1=9个节点空间,也就是宽度为9.
        res = Math.max(res, rIdx - lIdx + 1);
    }
    return res;
};
// @lc code=end

有道AI情报局
788 声望7.9k 粉丝

引用和评论

0 条评论