力扣关于树的题目(三)(一般二叉树+套路总结)

二叉树的属性问题

1. 101. 对称二叉树

检查自己,左子树,右子树

/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function(root) {
    const check = (left,right) => {
      if (left === null && right === null) {
          return true;
      }
      if (left === null || right === null) {
          return false;
      }

      let leftData = check(left.left,right.right);
      let rightData = check(right.left,left.right);

      return leftData && rightData && (left.val === right.val);
    }
    return check(root.left,root.right)
};
2. 104. 二叉树的最大深度

套路,左子树的深度和右子树的深度,最大的+1

/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxDepth = function(root) {
    let deep = 0;
    if (!root) {
        return deep;
    }
    const findDeep = (root) => {
      if (!root) {
          return 0;
      }
      let left = findDeep(root.left);
      let right = findDeep(root.right);
      
      return Math.max(left,right)+1;
    }
    return findDeep(root);
};
3. 111. 二叉树的最小深度

跟上一题一样,套路,左右两边小的+1

/**
 * @param {TreeNode} root
 * @return {number}
 */
var minDepth = function(root) {
    const findMin = (root) => {
        if(!root){
            return 0;
        }
        if(root.left == null && root.right == null){
            return 1;
        }
        let min = Infinity;
        if(root.left){
            min = Math.min(min,findMin(root.left))
        }
        if(root.right){
            min = Math.min(min,findMin(root.right))
        }
    
        return min + 1;
    }
    return findMin(root);
};
4. 222. 完全二叉树的节点个数

套路,左边+右边+1

/**
 * @param {TreeNode} root
 * @return {number}
 */
var countNodes = function(root) {
    const count = (root) => {
      if (!root) {
          return 0;
      }
      let leftData = count(root.left);
      let rightData = count(root.right);
      return leftData + rightData + 1;
    }
    
    return count(root);
};
5. 110. 平衡二叉树

套路,向左边要信息,右边要信息,再判断返回

/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isBalanced = function(root) {
    function returnData(is, height) {
        this.isBan = is;
        this.height = height;
    }
    const check = (root) => {
      if (!root){
          return new returnData(true,0);
      }
      let leftData = check(root.left);
      let rightData = check(root.right);

      let is = true;
      if (!leftData.isBan || !rightData.isBan || Math.abs(leftData.height-rightData.height) > 1) {
          is = false;
      }

      return new returnData(is,Math.max(leftData.height,rightData.height)+1);
    }

    return check(root).isBan;
};
6. 257. 二叉树的所有路径

这题有点不一样,像全排列,回溯

/**
 * @param {TreeNode} root
 * @return {string[]}
 */
var binaryTreePaths = function(root) {
    let res = [];
    let path = [];

    function changeToStr() {
        let str = "";
        for (let i = 0; i < path.length; i++) {
            if (i === path.length-1) {
                 str = str.concat(path[i]);
            } else {
                str = str.concat(path[i] + "->");
            }
        }
        return str;
    }

    const backtrace = (root) => {
      if (root) {
          path.push(root.val);
      }

      if (!root.left && !root.right){
          res.push(changeToStr());
          return;
      }

      if (root.left){
          backtrace(root.left)
          path.pop();
      }

      if (root.right) {
          backtrace(root.right);
          path.pop();
      }
    }

    backtrace(root);
    return res;
};
7. 404. 左叶子之和

这里实际上还是遍历,遍历到所有的节点,看它是否存在左节点,而且是叶子节点

/**
 * @param {TreeNode} root
 * @return {number}
 */
var sumOfLeftLeaves = function(root) {
    let sum = 0;
    function checkLeave(root) {
        if (!root.left && !root.right) {
            return true;
        }else {
            return false;
        }
    }
    const bianli = (root) => {
        if(!root){
            return;
        }
        if(root.left && checkLeave(root.left)){
            sum += root.left.val;
        }
        bianli(root.left);
        bianli(root.right);
    }
    bianli(root)
    return sum;
};
8. 112. 路径总和

回溯问题,后面应该会单独再归纳回溯的专题

/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {boolean}
 */
var hasPathSum = function(root, targetSum) {
    if(!root) {
        return false;
    }
    function checkLeave(root) {
        if (!root.left && !root.right){
            return true;
        }else {
            return false;
        }
    }
    // backtrace
    const check = (root,sum) => {
        if (checkLeave(root) && sum === 0){
            return true;
        }
      if (checkLeave(root)) {
          return false;
      }

      if (root.left) {
          if (check(root.left,sum- root.left.val)){
              return true;
          }
      }

      if (root.right) {
          if (check(root.right,sum- root.right.val)){
              return true;
          }
      }

      return false;
    }
    return check(root,targetSum-root.val);
};

二叉树的修改和构造问题

套路:

修改:找到对应的节点,修改左右子树

构造:先构造根节点,再递归构造左右子树

1. 226. 翻转二叉树

遍历,每一个节点都交换它的左右节点

/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var invertTree = function(root) {
    // if (!root) {
    //     return root;
    // }

    function swap(root) {
        let tmp = root.left;
        root.left = root.right;
        root.right = tmp;
    }

    const order = (root) => {
      if (!root) {
          return;
      }
      if (root.left || root.right) {
          swap(root);
      }
      order(root.left);
      order(root.right)
    }
    order(root);
    return root;
};
2. 106. 从中序与后序遍历序列构造二叉树

在后序找到根节点,再根据中序构造左右子树

/**
 * @param {number[]} inorder
 * @param {number[]} postorder
 * @return {TreeNode}
 */
var buildTree = function(inorder, postorder) {
    if (postorder.length === 0) {
        return null;
    }
    let root = new TreeNode(postorder[postorder.length-1]);
    let mid = inorder.findIndex((number) => number === root.val);
    root.left = buildTree(inorder.slice(0,mid),postorder.slice(0,mid));
    root.right = buildTree(inorder.slice(mid+1,inorder.length),postorder.slice(mid,postorder.length-1));

    return root;
};
3. 105. 从前序与中序遍历序列构造二叉树

跟上一题一样,在前序找到根节点,再根据中序构造左右子树

/**
 * @param {number[]} preorder
 * @param {number[]} inorder
 * @return {TreeNode}
 */
var buildTree = function(preorder, inorder) {
    if (preorder.length === 0) {
        return null;
    }
    let root = new TreeNode(preorder[0]);
    let mid = inorder.findIndex((number) => number === root.val);
    root.left = buildTree(preorder.slice(1,mid+1),inorder.slice(0,mid));
    root.right = buildTree(preorder.slice(mid+1,preorder.length),inorder.slice(mid+1,inorder.length));

    return root;
};
4. 654. 最大二叉树

套路,找到对应的节点构造根,再构造左右子树

/**
 * @param {number[]} nums
 * @return {TreeNode}
 */
var constructMaximumBinaryTree = function(nums) {
    function getMax(start, end) {
        let res = start;
        for (let i = start; i <= end; i++) {
            if (nums[i]>nums[res]) {
                res = i;
            }
        }
        return res;
    }
    const backtrace = (start=0,end=nums.length-1) => {
        if (start > end){
            return null;
        }
      let index = getMax(start,end);
      let root = new TreeNode(nums[index],backtrace(start,index-1),backtrace(index+1,end))

      return root;
    }
    return backtrace();
};
5. 617. 合并二叉树

套路,找到对应的节点构造根,再构造左右子树

/**
 * @param {TreeNode} root1
 * @param {TreeNode} root2
 * @return {TreeNode}
 */
var mergeTrees = function(root1, root2) {
    const backtrace = (node1,node2) => {
      if (!node1) {
          return node2;
      }
      if (!node2) {
          return node1;
      }
      let root = new TreeNode(node1.val+node2.val,backtrace(node1.left,node2.left),backtrace(node1.right,node2.right))

      return root;
    }
    return backtrace(root1,root2);
};

二叉树的祖先问题

1. 236. 二叉树的最近公共祖先
  • map存储所有的节点以及其父节点
  • 利用map记录p的所有祖宗节点,一直往上窜,直到root
  • 利用map找q的祖宗节点,直到找到的节点出现在p的祖宗节点中
/**
 * 最低公共祖先
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function(root, p, q) {
    let map = new Map();
    map.set(root,root);
    const preOrder = (root) => {
      if (!root) {
          return;
      }
      map.set(root.left,root);
      map.set(root.right,root);
      preOrder(root.left);
      preOrder(root.right);
    }
    preOrder(root);
    let hashset = [];
    let cur = p;
    while (cur !== map.get(cur)) {
        hashset.push(cur);
        cur = map.get(cur);
    }
    hashset.push(root)

    cur = q;
    while (cur !== map.get(cur)) {
        let index = hashset.findIndex((item) => item.val === cur.val)
        if (index === -1){
            cur = map.get(cur);
        }else {
            return hashset[index];
        }
    }
    return root;
};

优化的代码

  • 如果是p和q分别在root的左右两边,也就是说left和right都有值,那么root就是祖先
  • left和right有一方是没有值的,说明另一方就是这一方的祖先
/**
 * 最低公共祖先
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function(root, p, q) {
    const check = (root,p,q) => {
        if(!root || root === p || root === q) {
            return root;
        } 
        let left = check(root.left,p,q)
        let right = check(root.right,p,q);
        if(left && right) {
            return root;
        }
        return left !== null ? left : right;
    }

    return check(root,p,q)
};

二叉树的套路

  1. 定义数据类型
  2. 向左边要信息,要右边要信息
  3. 然后根据左右的信息,算出自己要返回的信息
posted @ 2022-04-21 16:27  kihyun  阅读(25)  评论(0编辑  收藏  举报