前端常见算法题(树篇)--下

196 阅读8分钟

三、特殊二叉树问题

2020.11.27

No.101 对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

 

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

   1   /
2   2 / \ /
3  4 4  3  

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

   1   /
2   2   \  
3    3  

进阶:

你可以运用递归和迭代两种方法解决这个问题吗?

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/sy… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

/*
 * @lc app=leetcode.cn id=101 lang=javascript
 *
 * [101] 对称二叉树
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function(root) {
    // 中序遍历+层数
    const inorderLevelTraversal = function(root) {
        const r = [];
        const recurse = ( node, n ) => {
            if(!node) return;
            n++;
            recurse(node.left, n);
            r.push([node.val,n])
            recurse(node.right, n);
        };

        recurse(root, 0);
        return r;
    };
    const r = inorderLevelTraversal(root);
    console.log('r', r);
    // 判断中序遍历数组是否对称
    let p1 = 0,
        p2 = r.length - 1;
    while( p1 < p2 ) {
        if( r[p1][0] != r[p2][0] || r[p1][1] != r[p2][1] ) return false;
        p1++;
        p2--;
    };
    return true;
};

方案二:

/*
 * @lc app=leetcode.cn id=101 lang=javascript
 *
 * [101] 对称二叉树
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
const isSymmetric = (root) => {
  
  const check = (left, right) => {
    if (left == null && right == null) { // 两个子树都为null,是对称的
      return true;
    }
    if (left && right) {  // 两个子树都存在,则需要:root值相同,且他们的子树也满足镜像
      return left.val == right.val && check(left.left, right.right) && check(left.right, right.left);
    }
    return false;         // 一个子树存在一个不存在,肯定不对称
  };

  if (root == null) {     // 如果传入的root就是null,对称
    return true;
  }           
  return check(root.left, root.right); // 否则,判断它的左右子树是否满足对称
};

方案三:

/*
 * @lc app=leetcode.cn id=101 lang=javascript
 *
 * [101] 对称二叉树
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
const isSymmetric = (root) => {
  if (root == null) return true; 

  const queue = [];
  queue.push(root.left, root.right);  // 起初入列两个子树

  while (queue.length) {  // 队列清空就结束,没有节点可入列了
    const levelSize = queue.length; // 当前层的节点个数
    for (let i = 0; i < levelSize; i += 2) { // 当前层的节点成对出列
      const left = queue.shift();   
      const right = queue.shift();  // 出列一对节点
      if ((left && right == null) || (left == null && right)) { // 有一个存在有一个不存在
        return false;
      }
      if (left && right) { // 两个都存在
        if (left.val != right.val) { // 节点值不同,不对称
          return false;
        }
        queue.push(left.left, right.right); // 推入下一层的一对节点
        queue.push(left.right, right.left); // 推入下一层的一对节点
      }
    }
  }
  return true; // bfs结束,始终没有返回false,则返回真
};

有三种解法:1、参考中序遍历思路,判断中序遍历后的数组是否对称,这里需要加入层数来避免有一个节点为null无法判断的情形;2、DFS递归,直接递归判断;3、BFS迭代,维护一个队列进行判断



2020.11.30

No.110 平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

 

示例 1: balance_1.jpg

输入:root = [3,9,20,null,null,15,7] 输出:true 示例 2: balance_2.jpg

输入:root = [1,2,2,3,3,null,null,4,4] 输出:false 示例 3:

输入:root = [] 输出:true  

提示:

树中的节点数在范围 [0, 5000] 内 -104 <= Node.val <= 104

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ba… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

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

// @lc code=start
/**
 * 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 isBalanced = function(root) {
    // 获取树的高度
    const getTreeHeight = tree => {
        if(!tree) return true;
        return 1 + Math.max(getTreeHeight(tree.left),getTreeHeight(tree.right))
    };
    // 差值比较后返回结果
    if(!root) return true;
    const lh = getTreeHeight(root.left),
          rh = getTreeHeight(root.right);
    // 如果结果有大于1的直接false
    if(Math.abs(lh-rh) > 1 ) {
        return false;    
    }
    return isBalanced(root.left) && isBalanced(root.right);
};

方案二:

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

// @lc code=start
/**
 * 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 isBalanced = function(root) {
    // 判断是否平衡
    const balanced = function (node) {
        if (!node) return 0
        const left = balanced(node.left)
        const right = balanced(node.right)
        if (left === -1 || right === -1 || Math.abs(left - right) > 1) {
            return -1
        }
        return Math.max(left, right) + 1
    }
    return balanced(root) !== -1;
};

有两种方案:1、自顶向下:获取左子树和右子树高度比较,如果不符合直接返回false,否则向下递归;2、自底向上:类似后序遍历方案,先判断左子树,再判断右子树,再判断根节点,有不符合就返回false,否则向上递归



2020.12.01

No.226 翻转二叉树

翻转一棵二叉树。

示例:

输入:

    4   /  
2     7 / \   /
1   3 6   9 输出:

    4   /  
7     2 / \   /
9   6 3   1 备注: 这个问题是受到 Max Howell 的 原问题 启发的 :

谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/in… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

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

// @lc code=start
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var invertTree = function(root) {
    let temp = null;
    const recurse = node => {
        if(!node) return;
        temp = node.left;
        node.left = node.right;
        node.right = temp;
        recurse(node.left);
        recurse(node.right);
    };
    recurse(root);
    return root;
};

方案二:

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

// @lc code=start
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var invertTree = function(root) {
  if (!root) return root;
  const queue = [root];   // 维护一个队列,初始推入第一层的root
  
  while (queue.length) {
    const cur = queue.shift(); // 出列的节点
    [cur.left, cur.right] = [cur.right, cur.left]; // 交换左右子树

    cur.left && queue.push(cur.left);
    cur.right && queue.push(cur.right);
  }
  
  return root;
};

面试考树最常考的题,两种方案:1、递归;2、迭代



2020.12.02

No.617 合并二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

示例 1:

输入: Tree 1                     Tree 2                            1                         2                                     / \                       / \                                    3   2                     1   3                               /                           \   \                            5                             4   7                   输出: 合并后的树:      3     /
4   5   / \   \  5   4   7 注意: 合并必须从两个树的根节点开始。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/me… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案一:

/*
 * @lc app=leetcode.cn id=617 lang=javascript
 *
 * [617] 合并二叉树
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} t1
 * @param {TreeNode} t2
 * @return {TreeNode}
 */
var mergeTrees = function(t1, t2) {
    // 以t1为基准
    if(!t1) return t2;
    if(!t2) return t1;
    t1.val = t2.val + t1.val;
    t1.left = mergeTrees(t1.left, t2.left);
    t1.right = mergeTrees(t1.right, t2.right);
    return t1;
};

方案二:

/*
 * @lc app=leetcode.cn id=617 lang=javascript
 *
 * [617] 合并二叉树
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} t1
 * @param {TreeNode} t2
 * @return {TreeNode}
 */
var mergeTrees = function(t1, t2) {
    // 以t1为基准
    if(!t1) return t2;
    if(!t2) return t1;
    t1.val = t2.val + t1.val;
    // 构造栈空间迭代
    const stack = [[t1, t2]];
    while (stack.length) {
        const [p, q] = stack.shift();
        if (p.left && q.left) {
            p.left.val += q.left.val
            stack.push([p.left, q.left]);
        } else if (!p.left) p.left = q.left;
        else if (!q.left) q.left = p.left;

        if (p.right && q.right) {
            p.right.val += q.right.val;
            stack.push([p.right, q.right]);
        } else if (!p.right) p.right = q.right;
        else if (!q.right) q.right = p.right;
    }
    return t1;
};

同226,树的基本操作,面试树常考,有两种方案:1、递归;2、迭代



2020.12.03

No.654 最大二叉树

给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:

二叉树的根是数组中的最大元素。 左子树是通过数组中最大值左边部分构造出的最大二叉树。 右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构建最大二叉树,并且输出这个树的根节点。

 

示例 :

输入:[3,2,1,6,0,5] 输出:返回下面这棵树的根节点:

     6    /  
3     5    \    /     2  0        
1  

提示:

给定的数组的大小在 [1, 1000] 之间。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ma… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=654 lang=javascript
 *
 * [654] 最大二叉树
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {number[]} nums
 * @return {TreeNode}
 */
var constructMaximumBinaryTree = function(nums) {
    if(nums.length == 0) return null;
    const root = new TreeNode(Math.max(...nums));
    root.left = constructMaximumBinaryTree(nums.slice(0,nums.indexOf(Math.max(...nums))));
    root.right = constructMaximumBinaryTree(nums.slice(nums.indexOf(Math.max(...nums))+1));
    return root;
};

典型的递归问题,以最大值作为分割点



2020.12.04

No.655 输出二叉树

在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则:

行数 m 应当等于给定二叉树的高度。 列数 n 应当总是奇数。 根节点的值(以字符串格式给出)应当放在可放置的第一行正中间。根节点所在的行与列会将剩余空间划分为两部分(左下部分和右下部分)。你应该将左子树输出在左下部分,右子树输出在右下部分。左下和右下部分应当有相同的大小。即使一个子树为空而另一个非空,你不需要为空的子树输出任何东西,但仍需要为另一个子树留出足够的空间。然而,如果两个子树都为空则不需要为它们留出任何空间。 每个未使用的空间应包含一个空的字符串""。 使用相同的规则输出子树。 示例 1:

输入:     1    /   2 输出: [["", "1", ""], ["2", "", ""]] 示例 2:

输入:     1    /
2   3    
4 输出: [["", "", "", "1", "", "", ""], ["", "2", "", "", "", "3", ""], ["", "", "4", "", "", "", ""]] 示例 3:

输入:      1     /
2   5   /  3 / 4 输出: [["",  "",  "", "",  "", "", "", "1", "",  "",  "",  "",  "", "", ""] ["",  "",  "", "2", "", "", "", "",  "",  "",  "",  "5", "", "", ""] ["",  "3", "", "",  "", "", "", "",  "",  "",  "",  "",  "", "", ""] ["4", "",  "", "",  "", "", "", "",  "",  "",  "",  "",  "", "", ""]] 注意: 二叉树的高度在范围 [1, 10] 中。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/pr… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=655 lang=javascript
 *
 * [655] 输出二叉树
 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {string[][]}
 */
var printTree = function(root) {
    // 获取树的高度
    const treeHeight = root => {
        if(!root) return 0;
        return treeHeight(root.left) > treeHeight(root.right) ? ( treeHeight(root.left) + 1 ): (treeHeight(root.right) + 1);
    };
    // 填充输出值
    const printValue = (r, root, n, left, right ) => {
        if(root) {
        let mid = Math.floor( ( left + right ) / 2 );
        r[n][mid] = root.val + '';
        printValue(r, root.left, n+1, left, mid);
        printValue(r, root.right, n+1, mid+1, right)}
    };

    const m = treeHeight(root),
          n = Math.pow(2,m) - 1;
    console.log(m)
    const r = new Array(m);
    for(let i=0;i<m;i++) r[i]=new Array(n).fill('');
    printValue(r, root,0,0,n);
    return r;
};

二分法递归,先生成二维数组,再在对应位置填充值



2020.12.06

No.998 最大二叉树-ii

最大树定义:一个树,其中每个节点的值都大于其子树中的任何其他值。

给出最大树的根节点 root。

就像之前的问题那样,给定的树是从表 A(root = Construct(A))递归地使用下述 Construct(A) 例程构造的:

如果 A 为空,返回 null 否则,令 A[i] 作为 A 的最大元素。创建一个值为 A[i] 的根节点 root root 的左子树将被构建为 Construct([A[0], A[1], ..., A[i-1]]) root 的右子树将被构建为 Construct([A[i+1], A[i+2], ..., A[A.length - 1]]) 返回 root 请注意,我们没有直接给定 A,只有一个根节点 root = Construct(A).

假设 B 是 A 的副本,并附加值 val。保证 B 中的值是不同的。

返回 Construct(B)。

 

示例 1: maximum-binary-tree-1-1.pngmaximum-binary-tree-1-2.png

输入:root = [4,1,3,null,null,2], val = 5 输出:[5,4,null,1,3,null,null,2] 解释:A = [1,4,2,3], B = [1,4,2,3,5] 示例 2: maximum-binary-tree-2-1.pngmaximum-binary-tree-2-2.png

输入:root = [5,2,4,null,1], val = 3 输出:[5,2,4,null,1,null,3] 解释:A = [2,1,5,4], B = [2,1,5,4,3] 示例 3:

输入:root = [5,2,3,null,1], val = 4 输出:[5,2,4,null,1,3] 解释:A = [2,1,5,3], B = [2,1,5,3,4]  

提示:

1 <= B.length <= 100

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ma… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=998 lang=javascript
 *
 * [998] 最大二叉树 II
 */

// @lc code=start
/**
 * 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
 * @param {number} val
 * @return {TreeNode}
 */
var insertIntoMaxTree = function(root, val) {
    // 中序遍历
    const inorderTraversal = function(root) {
        let r = [];
        // 递归函数
        const recurse = root => {
            // 递归终止条件
            if(!root) return;
            // 先遍历左子树
            recurse(root.left);
            // 遇到终止条件,此时的val是符合终止条件的值
            r.push(root.val);
            // 再遍历右子树
            recurse(root.right);
        };
        recurse(root);
        return r;
    };

    // 数组的最大二叉树
    const constructMaximumBinaryTree = function(nums) {
        if(nums.length == 0) return null;
        const root = new TreeNode(Math.max(...nums));
        root.left = constructMaximumBinaryTree(nums.slice(0,nums.indexOf(Math.max(...nums))));
        root.right = constructMaximumBinaryTree(nums.slice(nums.indexOf(Math.max(...nums))+1));
        return root;
    };

    const A = inorderTraversal(root);
    A.push(val);
    return constructMaximumBinaryTree(A);
};

94题和655题的综合,先求出中序遍历数组,在数组后添加val值,对新数组进行求最大树



总结:

  1. 特殊二叉树主要是树的不同形态的处理,常见的主要是递归和迭代,面试中常常要求都写出来;
  2. 根据不同要求获取树,算是树的基本考察,面试过程中如果考树,一般都会以特殊二叉树来检验