数据结构-二叉树三

226 阅读19分钟

树的子结构

树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)B是A的子结构, 即 A中有出现和B相同的结构和节点值。

输入:A = [3,4,5,1,2], B = [4,1]
输出:true

来源: leetcode-cn.com/problems/sh…

var isSubStructure = function(A, B) {
    if(!A || !B) return false;
    return isSunTree(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
};

function isSunTree(A, B) {
    if(!B) return true;
    if(!A) return false;
    if(A.val !== B.val) return false;
    return isSunTree(A.left, B.left) && isSunTree(A.right, B.right);
}

另一个树的子树

给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。

来源: leetcode-cn.com/problems/su…

var isSubtree = function(s, t) {
    if(!s) return false;
    if(isSame(s, t)) return true; 
    return isSubtree(s.left, t) || isSubtree(s.right, t);   
};

function isSame(s, t) {
    if(!s && !t) return true;
    if(!s || !t) return false;
    if(s.val !== t.val) return false;
    return isSame(s.left, t.left) && isSame(s.right, t.right);
}

寻找重复的子树

给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。两棵树重复是指它们具有相同的结构以及相同的结点值。

来源: leetcode-cn.com/problems/fi…

var findDuplicateSubtrees = function(root) {
    let res = [], h = {};
    function dfs(root) {
        if(!root) return '#';
        let cur = "";
        cur += String(root.val) + "," + dfs(root.left) + "," + dfs(root.right);
        h[cur] ? h[cur]++ : h[cur] = 1;
        if(h[cur] === 2) {
            res.push(root);
        }
        return cur;
    }
    dfs(root);

    return res;   
};

具有所有最深节点的最小子树

给定一个根为 root 的二叉树,每个节点的深度是 该节点到根的最短距离 。 如果一个节点在 整个树 的任意节点之间具有最大的深度,则该节点是 最深的 。 一个节点的 子树 是该节点加上它的所有后代的集合。 返回能满足 以该节点为根的子树中包含所有最深的节点 这一条件的具有最大深度的节点。

输入:root = [3,5,1,6,2,0,8,null,null,7,4]
输出:[2,7,4]
解释:
我们返回值为 2 的节点,在图中用黄色标记。
在图中用蓝色标记的是树的最深的节点。
注意,节点 5、3 和 2 包含树中最深的节点,但节点 2 的子树最小,因此我们返回它。

来源: leetcode-cn.com/problems/sm…

var subtreeWithAllDeepest = function(root) {
    function getMaxDph(root) {
        if(!root) return 0;
        return Math.max(getMaxDph(root.left), getMaxDph(root.right)) + 1;
    }
    if(!root) return null;
    let leftDph = getMaxDph(root.left);
    let rightDph = getMaxDph(root.right);
    if(leftDph === rightDph) return root;
    else if(leftDph > rightDph) {
        return subtreeWithAllDeepest(root.left);
    } else {
        return subtreeWithAllDeepest(root.right);
    }
};

特殊二叉树

单值二叉树

如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。只有给定的树是单值二叉树时,才返回 true;否则返回 false

输入:[1,1,1,1,1,null,1]
输出:true

来源: leetcode-cn.com/problems/un…

var isUnivalTree = function(root) {
    let queue = [root];
    while(queue.length > 0) {
        let node = queue.shift();
        if(node.val !== root.val) return false;
        if(node.left) queue.push(node.left);
        if(node.right) queue.push(node.right);
    }
    return true;
};

二叉树的镜像

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

来源: leetcode-cn.com/problems/er…

var mirrorTree = function(root) {
    if(root) {
        let tmp = root.left;
        root.left = mirrorTree(root.right);
        root.right = mirrorTree(tmp)
    }
    return root;
};

叶子相似的树

输入:root1 = [3,5,1,6,2,9,8,null,null,7,4], 
     root2 = [3,5,1,6,7,4,2,null,null,null,null,null,null,9,8]
输出:true

来源: leetcode-cn.com/problems/le…

var leafSimilar = function(root1, root2) {
    let arr1 = [], arr2 = [], ans = true;
    let help = (node, arr) => {
        if(!node) return;
        help(node.left, arr);
        help(node.right, arr);
        if(!node.left && !node.right) {
            arr.push(node.val);
        }
    }
    help(root1, arr1);
    help(root2, arr2);
    if(arr1.length !== arr2.length) return false;
    for (let i = 0; i < arr1.length; i++) {
        if(arr1[i] !== arr2[i]) {
            ans = false;
            break;
        }
    }
    return ans;
};

相同的树

给定两个二叉树,编写一个函数来检验它们是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的

输入:       1         1
          / \       / \
         2   3     2   3

        [1,2,3],   [1,2,3]

输出: true

来源: leetcode-cn.com/problems/sa…

var isSameTree = function(p, q) {
    if(!p && !q) return true;
    if(!p || !q) return false;
    if(p.val !== q.val) return false;
    return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);    
};

合并二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。  

输入: 
	Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
输出: 
合并后的树:
	     3
	    / \
	   4   5
	  / \   \ 
	 5   4   7

来源: leetcode-cn.com/problems/me…

var mergeTrees = function(t1, t2) {
    if(!t1 && !t2) return null;
    if(!t1 && t2) return t1 = t2;
    if(t1 && t2) t1.val += t2.val;
    t1.left = mergeTrees(t1.left, t2 && t2.left);
    t1.right = mergeTrees(t1.right, t2 && t2.right);
    return t1;
};

最大二叉树

给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下: 二叉树的根是数组中的最大元素。 左子树是通过数组中最大值左边部分构造出的最大二叉树。 右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构建最大二叉树,并且输出这个树的根节点。  

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

      6
    /   \
   3     5
    \    / 
     2  0   
       \
        1

来源: leetcode-cn.com/problems/ma…

var constructMaximumBinaryTree = function(nums) {
    if(!nums.length) return null;
    let idx = 0;
    let max = Math.max(...nums);
    const maxIdx = nums.indexOf(max);
    const left = nums.slice(0, maxIdx);
    const right = nums.slice(maxIdx + 1);
    const root = new TreeNode(max);
    root.left = constructMaximumBinaryTree(left);
    root.right = constructMaximumBinaryTree(right);
    return root;
};

最大二叉树 II

输入: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]

来源: leetcode-cn.com/problems/ma…

var insertIntoMaxTree = function(root, val) {
    if(!root || val > root.val) {
        let tmp = new TreeNode(val);
        tmp.left = root;
        return tmp;
    } else {
        root.right = insertIntoMaxTree(root.right, val);
        return root;
    }
};

二叉树的完全性检验

给定一个二叉树,确定它是否是一个完全二叉树。 

 百度百科中对完全二叉树的定义如下: 若设二叉树的深度为 h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。(注:第 h 层可能包含 1~ 2h 个节点。)  

输入:[1,2,3,4,5,6]
输出:true
解释:最后一层前的每一层都是满的(即,结点值为 {1} 和 {2,3} 的两层),且最后一层中的所有结点({4,5,6})都尽可能地向左。

来源:leetcode-cn.com/problems/ch…

// 层序遍历(队列实现)
//1,如果node.left不为null,node.left入队
//2,如果node.left == null && node.right !=  null, 返回false;
//3,如果node.right != null, 将node.right入队
//4,如果node.right == null;
//  那么后面遍历的节点应该都为叶子节点,才是完全二叉树
//  否则返回false
//5遍历结束,返回false;
var isCompleteTree = function(root) {
    if (!root) return false;
    let queue = [root];
    let leaf = false;
    while(queue.length > 0) {
        let node = queue.shift();
        if(leaf && !isLeaf(node)) return false;
        if(node.left != null) {
            queue.push(node.left);
        } else if(node.right !== null) {
            return false;
        }
        if(node.right != null) {
            queue.push(node.right);
        } else {
            leaf = true;
        }
    }
    return true;
    function isLeaf(node) {
        return node.left === null && node.right === null;
    }
};

树的节点

二叉树的堂兄弟节点

在二叉树中,根节点位于深度 0 处,每个深度为 k 的节点的子节点位于深度 k+1 处。 如果二叉树的两个节点深度相同,但父节点不同,则它们是一对堂兄弟节点。 我们给出了具有唯一值的二叉树的根节点 root,以及树中两个不同节点的值 x 和 y。 只有与值 x 和 y 对应的节点是堂兄弟节点时,才返回 true。否则,返回 false。

输入:root = [1,2,3,null,4,null,5], x = 5, y = 4
输出:true

来源: leetcode-cn.com/problems/co…

var isCousins = function(root, x, y) {
    let depth = new Map(), parents = new Map();
    let help = (root, parent) => {
        depth.set(root.val, parent === null ? 0 : depth.get(parent.val) + 1);
        parents.set(root.val, parent);
        if(root.left) help(root.left, root);
        if(root.right) help(root.right, root);
    }
    help(root, null);
    return depth.get(x) === depth.get(y) && parents.get(x) !== parents.get(y)
};

统计二叉树中好节点的数目

给你一棵根为 root 的二叉树,请你返回二叉树中好节点的数目。「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。

输入:root = [3,1,4,3,null,1,5]
输出:4
解释:图中蓝色节点为好节点。
根节点 (3) 永远是个好节点。
节点 4 -> (3,4) 是路径中的最大值。
节点 5 -> (3,4,5) 是路径中的最大值。
节点 3 -> (3,1,3) 是路径中的最大值。

来源: leetcode-cn.com/problems/co…

var goodNodes = function(root) {
    let count = 0;
    dfs(root, root.val);
    return count;
    function dfs(root, max) {
        if(!root) return;
        if(root.val >= max) {
            count++;
            max = root.val;
        }
        dfs(root.left, max);
        dfs(root.right, max);
    }
};

完全二叉树的节点个数

给出一个完全二叉树,求出该树的节点个数。

输入: 
    1
   / \
  2   3
 / \  /
4  5 6

输出: 6

来源: leetcode-cn.com/problems/co…

var countNodes = function(root) {
    if(!root) return 0;
    return countNodes(root.left) + countNodes(root.right) + 1;
};

二叉树中所有距离为 K 的结点

给定一个二叉树(具有根结点 root), 一个目标结点 target ,和一个整数值 K 。 返回到目标结点 target 距离为 K 的所有结点的值的列表。 答案可以以任何顺序返回。  

输入:root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2
输出:[7,4,1]
解释:
所求结点为与目标结点(值为 5)距离为 2 的结点,
值分别为 74,以及 1

注意,输入的 "root""target" 实际上是树上的结点。
上面的输入仅仅是对这些对象进行了序列化描述。

来源: leetcode-cn.com/problems/al…

var distanceK = function(root, target, k) {
    if(!root) return [];
    let targetNode = null;
    let res = [];
    let paths = [];

    // 找到target节点,存储到targetNode中
    dfs(root, target);
    // 从当前节点向下寻找
    getdownDis(targetNode, k);
    // 从当前节点向上寻找
    while(targetNode.parent && k>0){
        targetNode = targetNode.parent;
        getdownDis(targetNode, --k);
    }

    function dfs(_root, target){
        if(!_root || targetNode) return;
        if(_root.val === target.val){
            targetNode = _root;
        }
        if(_root.left){
            _root.left.parent = _root;
            dfs(_root.left, target);
        }
        if(_root.right){
            _root.right.parent = _root;
            dfs(_root.right, target);
        }
    }

    function getdownDis(node, k){
        if(node === null || paths.indexOf(node) !== -1) return;
        paths.push(node);
        if(k>0){
            getdownDis(node.left, k-1);
            getdownDis(node.right, k-1);  
        }else if(k === 0){
            res.push(node.val);
        }
    }

    return res;

};

特定深度节点链表

给定一棵二叉树,设计一个算法,创建含有某一深度上所有节点的链表(比如,若一棵树的深度为 D,则会创建出 D 个链表)。返回一个包含所有深度的链表的数组。

输入:[1,2,3,4,5,null,7,8]

        1
       /  \ 
      2    3
     / \    \ 
    4   5    7
   /
  8

输出:[[1],[2,3],[4,5,7],[8]]

来源: leetcode-cn.com/problems/li…

var listOfDepth = function(tree) {
    if(!tree) return null;
    let ans = [];
    let queue = [tree];
    while(queue.length) {
        let dummyHead = { next: null }, tail = dummyHead;
        for (let i = 0, n = queue.length; i < n; i++) {
            let node = queue.shift();
            tail = tail.next = new ListNode(node.val);
            if(node.left) queue.push(node.left);
            if(node.right) queue.push(node.right);
        }
        ans.push(dummyHead.next)
    }
    return ans;
};

从叶结点开始的最小字符串

给定一颗根结点为 root 的二叉树,树中的每一个结点都有一个从 0 到 25 的值,分别代表字母 'a' 到 'z':值 0 代表 'a',值 1 代表 'b',依此类推。 找出按字典序最小的字符串,该字符串从这棵树的一个叶结点开始,到根结点结束。  

输入:[0,1,2,3,4,3,4]
输出:"dba"

来源: leetcode-cn.com/problems/sm…

var smallestFromLeaf = function(root) {
    if(!root) return '';
    let res = [];
    let dfs = (root, res, str) => {
        if(!root) return '';
        let s = String.fromCharCode('a'.charCodeAt() + root.val);
        if(!root.left && !root.right) {
            res.push(s + str)
        }
        if(root.left) {
            dfs(root.left, res, s + str)
        }
        if(root.right) {
            dfs(root.right, res, s + str)
        }
    }
    dfs(root, res, '');
    return res.sort()[0]
};

二叉树中第二小的节点

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。 更正式地说,root.val = min(root.left.val, root.right.val) 总成立。 给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。  

输入:root = [2,2,5,null,null,5,7]
输出:5
解释:最小的值是 2 ,第二小的值是 5

来源: leetcode-cn.com/problems/se…

var findSecondMinimumValue = function(root) {
    let res = [];
    let help = (root) => {
        if(!root) return;
        res.push(root.val);
        help(root.left);
        help(root.right);
    }
    help(root);
    res.sort((a, b) => a - b);
    return [...new Set(res)][1] || -1;
};

祖父节点值为偶数的节点和

给你一棵二叉树,请你返回满足以下条件的所有节点的值之和: 该节点的祖父节点的值为偶数。(一个节点的祖父节点是指该节点的父节点的父节点。) 如果不存在祖父节点值为偶数的节点,那么返回 0 。  

输入:root = [6,7,8,2,7,1,3,9,null,1,4,null,null,null,5]
输出:18
解释:图中红色节点的祖父节点的值为偶数,蓝色节点为这些红色节点的祖父节点。

来源: leetcode-cn.com/problems/su…

var sumEvenGrandparent = function(root) {
    let tmp = 0;
    if(!root) return tmp;
    if(root.val % 2 === 0) {
        if(root.left) {
            if(root.left.left) tmp += root.left.left.val;
            if(root.left.right) tmp += root.left.right.val;
        }
        if(root.right) {
            if(root.right.left) tmp += root.right.left.val;
            if(root.right.right) tmp += root.right.right.val;
        }
    }
    return tmp + sumEvenGrandparent(root.left) + sumEvenGrandparent(root.right);
};

从根到叶的二进制数之和

给出一棵二叉树,其上每个结点的值都是 0 或 1 。每一条从根到叶的路径都代表一个从最高有效位开始的二进制数。例如,如果路径为 0 -> 1 -> 1 -> 0 -> 1,那么它表示二进制数 01101,也就是 13 。 对树上的每一片叶子,我们都要找出从根到该叶子的路径所表示的数字。  

输入:[1,0,1,0,1,0,1]
输出:22
解释:(100) + (101) + (110) + (111) = 4 + 5 + 6 + 7 = 22

来源: leetcode-cn.com/problems/su…

var sumRootToLeaf = function(root) {
    let sum = 0;
    let dfs = (root, tmp) => {
        if(!root) return;
        if(!root.left && !root.right) sum += tmp * 2 + root.val;
        if(root.left) dfs(root.left, tmp * 2 + root.val)
        if(root.right) dfs(root.right, tmp * 2 + root.val)
    }
    dfs(root, 0)
    return sum;
};

最近公共祖先问题

二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6

来源: leetcode-cn.com/problems/lo…

var lowestCommonAncestor = function(root, p, q) {
    if(root === null) return null;
    if(p.val < root.val && q.val < root.val) {
        return lowestCommonAncestor(root.left, p, q);
    }
    if(p.val > root.val && q.val > root.val) {
        return lowestCommonAncestor(root.right, p, q);
    }
    return root;
};

二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3

来源: leetcode-cn.com/problems/lo…

var lowestCommonAncestor = function(root, p, q) {
    if(!root || root === p || root === q) return root;
    const left = lowestCommonAncestor(root.left, p, q);
    const right = lowestCommonAncestor(root.right, p, q);
    if(!left) return right;
    if(!right) return left;
    return root;
};

二叉树的一些计算

二叉树的坡度

给定一个二叉树,计算整个树的坡度。 一个树的节点的坡度定义即为,该节点左子树的结点之和和右子树结点之和的差的绝对值。空结点的的坡度是0。 整个树的坡度就是其所有节点的坡度之和。 

输入:
         1
       /   \
      2     3
输出:1
解释:
结点 2 的坡度: 0
结点 3 的坡度: 0
结点 1 的坡度: |2-3| = 1
树的坡度 : 0 + 0 + 1 = 1

来源: leetcode-cn.com/problems/bi…

var findTilt = function(root) {
    let res = 0;
    let sumTree = (root) => {
        if(!root) return 0;
        let left = sumTree(root.left);
        let right = sumTree(root.right);
        res = res + Math.abs(left - right);
        return left + right + root.val;
    }
    sumTree(root);
    return res;
};

分裂二叉树的最大乘积

给你一棵二叉树,它的根为 root 。请你删除 1 条边,使二叉树分裂成两棵子树,且它们子树和的乘积尽可能大。

输入:root = [1,2,3,4,5,6]
输出:110
解释:删除红色的边,得到 2 棵子树,和分别为 11 和 10 。它们的乘积是 110 (11*10)

来源: leetcode-cn.com/problems/ma…

var maxProduct = function(root) {
    function postOrder(root) {
        if(!root) return 0;
        let res = root.val + postOrder(root.left) + postOrder(root.right);
        sums.push(res);
        return res;
    } 
    let res = -1, sums = [];
    postOrder(root);
    for (let i = 0; i < sums.length; i++) {
        res = Math.max(res, sums[i] * (sums[sums.length - 1] - sums[i]));
    }
    return res % (1e9 + 7)
};

后继者

设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。如果指定节点没有对应的“下一个”节点,则返回null

输入: root = [2,1,3], p = 1

  2
 / \
1   3

输出: 2

来源: leetcode-cn.com/problems/su…

var inorderSuccessor = function(root, p) {
    if(!root || !p) return null;
    if(p.val >= root.val) {
        return inorderSuccessor(root.right, p);
    } else {
        let left = inorderSuccessor(root.left, p);
        return left ? left : root;
    }
};

找树左下角的值

给定一个二叉树,在树的最后一行找到最左边的值。

输入:

        1
       / \
      2   3
     /   / \
    4   5   6
       /
      7

输出:
7

来源: leetcode-cn.com/problems/fi…

var findBottomLeftValue = function(root) {
    let queue = [root];
    while(queue.length) {
        root = queue.shift();
        if(root.right) queue.push(root.right);
        if(root.left) queue.push(root.left);
    }
    return root.val;
};

根据二叉树创建字符串

你需要采用前序遍历的方式,将一个二叉树转换成一个由括号和整数组成的字符串。空节点则用一对空括号 "()" 表示。而且你需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。

输入: 二叉树: [1,2,3,4]
       1
     /   \
    2     3
   /    
  4     

输出: "1(2(4))(3)"

解释: 原本将是“1(2(4)())(3())”,
在你省略所有不必要的空括号对之后,
它将是“1(2(4))(3)”。

来源: leetcode-cn.com/problems/co…

var tree2str = function(t) {
    if(t === null) return "";
    if(!t.left && !t.right) return t.val + "";
    if(!t.right) {
        return t.val + '(' + tree2str(t.left) + ')';
    }
    return t.val + '(' + tree2str(t.left) + ')(' + tree2str(t.right) + ')';
};

其他

在二叉树中分配硬币

给定一个有 N 个结点的二叉树的根结点 root,树中的每个结点上都对应有 node.val 枚硬币,并且总共有 N 枚硬币。 在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点。(移动可以是从父结点到子结点,或者从子结点移动到父结点。)。 返回使每个结点上只有一枚硬币所需的移动次数。 

输入:[3,0,0]
输出:2
解释:从树的根结点开始,我们将一枚硬币移到它的左子结点上,一枚硬币移到它的右子结点上。

来源: leetcode-cn.com/problems/di…

var distributeCoins = function(root) {
    let ans = 0;
    dfs(root);
    return ans;
    function dfs(root) {
        if(!root) return 0;
        let L = dfs(root.left);
        let R = dfs(root.right);
        ans += Math.abs(L) + Math.abs(R);
        return root.val + L + R - 1;
    }
};

二叉树剪枝

给定二叉树根结点 root ,此外树的每个结点的值要么是 0,要么是 1。 返回移除了所有不包含 1 的子树的原二叉树。 ( 节点 X 的子树为 X 本身,以及所有 X 的后代。)

示例1:
输入: [1,null,0,0,1]
输出: [1,null,0,null,1]

解释: 
只有红色节点满足条件“所有不包含 1 的子树”。
右图为返回的答案。

来源: leetcode-cn.com/problems/bi…

var pruneTree = function(root) {
    if(!root) return null;
    root.left = pruneTree(root.left);
    root.right = pruneTree(root.right);
    return (!root.left && !root.right && root.val === 0) ? null : root;
};

监控二叉树

给定一个二叉树,我们在树的节点上安装摄像头。节点上的每个摄影头都可以监视**其父对象、自身及其直接子对象。**计算监控树的所有节点所需的最小摄像头数量。

输入:[0,0,null,0,0]
输出:1
解释:如图所示,一台摄像头足以监控所有节点。

来源: leetcode-cn.com/problems/bi…

var minCameraCover = function(root) {
    let ans = 0;
    if(!root) return 0;
    if(dfs(root) === 2) ans++;
    return ans;
    function dfs(node) {
        if(!node) return 1;
        let left = dfs(node.left), right = dfs(node.right);
        if(left === 2 || right === 2) {
            ans++;
            return 0
        } else if(left === 0 || right === 0) {
            return 1;
        } else {
            return 2;
        }
    }
};

在二叉树中增加一行

给定一个二叉树,根节点为第1层,深度为 1。在其第 d 层追加一行值为 v 的节点。 添加规则:给定一个深度值 d (正整数),针对深度为 d-1 层的每一非空节点 N,为 N 创建两个值为 v 的左子树和右子树。 将 N 原先的左子树,连接为新节点 v 的左子树;将 N 原先的右子树,连接为新节点 v 的右子树。 如果 d 的值为 1,深度 d - 1 不存在,则创建一个新的根节点 v,原先的整棵树将作为 v 的左子树。

输入: 
二叉树如下所示:
       4
     /   \
    2     6
   / \   / 
  3   1 5   

v = 1

d = 2

输出: 
       4
      / \
     1   1
    /     \
   2       6
  / \     / 
 3   1   5   

来源: leetcode-cn.com/problems/ad…

var addOneRow = function(root, v, d) {
    let tmp;
    if(d === 1) {
        tmp = new TreeNode(v);
        tmp.left = root;
        return tmp;
    }
    add(root, 1);
    return root;
    function add(r, i) {
        if(r) {
            if(i < d - 1) {
                add(r.left, i + 1);
                add(r.right, i + 1);
            } else {
                tmp = new TreeNode(v);
                tmp.left = r.left;
                r.left = tmp;
                tmp = new TreeNode(v);
                tmp.right = r.right;
                r.right = tmp;
            }
        }
    }
};

二叉树展开为链表

给定一个二叉树,原地将它展开为一个单链表。

    1
   / \
  2   5
 / \   \
3   4   6
1
 \
  2
   \
    3
     \
      4
       \
        5
         \
          6

来源: leetcode-cn.com/problems/fl…

var flatten = function(root) {
    let dfs = (root) => {
        if(!root) return;
        dfs(root.left);
        dfs(root.right);
        let pre = root.left;
        if(pre) {
            while(pre.right) {
                pre = pre.right;
            }
            pre.right = root.right;
            root.right = root.left;
            root.left = null;
        }
    }  
    dfs(root);
    return root;  
};

递增顺序查找树

给你一个树,请你 按中序遍历 重新排列树,使树中最左边的结点现在是树的根,并且每个结点没有左子结点,只有一个右子结点。

输入:[5,3,6,2,4,null,8,1,null,null,null,7,9]

       5
      / \
    3    6
   / \    \
  2   4    8
 /        / \ 
1        7   9

输出:[1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9]

 1
  \
   2
    \
     3
      \
       4
        \
         5
          \
           6
            \
             7
              \
               8
                \
                 9  

来源: leetcode-cn.com/problems/in…

var increasingBST = function(root) {
    let arr = [];
    let help = (node) => {
        if(!node) return;
        help(node.left);
        arr.push(node.val);
        help(node.right)
    }
    help(root);
    let ans = new TreeNode(null);
    let cur = ans;
    while(arr.length > 0) {
        cur.right = new TreeNode(arr.shift());
        cur = cur.right;
    }
    return ans.right;
};

翻转等价二叉树

我们可以为二叉树 T 定义一个翻转操作,如下所示:选择任意节点,然后交换它的左子树和右子树。 只要经过一定次数的翻转操作后,能使 X 等于 Y,我们就称二叉树 X 翻转等价于二叉树 Y。 编写一个判断两个二叉树是否是翻转等价的函数。这些树由根节点 root1 和 root2 给出。 

输入:root1 = [1,2,3,4,5,6,null,null,null,7,8], root2 = [1,3,2,null,6,4,5,null,null,null,null,8,7]
输出:true
解释:我们翻转值为 13 以及 5 的三个节点。

来源:  leetcode-cn.com/problems/fl…

var flipEquiv = function(root1, root2) {
    if(!root1 && !root2) return true;
    if(!root1 || !root2) return false;
    if(root1.val === root2.val) {
        return ((flipEquiv(root1.left, root2.left) && flipEquiv(root1.right, root2.right)) 
       || (flipEquiv(root1.left, root2.right) && flipEquiv(root1.right, root2.left)))
    }
    return false;
};

翻转二叉树

翻转一棵二叉树。

     4
   /   \
  2     7
 / \   / \
1   3 6   9

来源: leetcode-cn.com/problems/in…

层序遍历实现

var invertTree = function(root) {
    let queue = [root];
    while(queue.length) {
        let cur = queue.pop();
        if(cur === null) continue;
        [cur.left, cur.right] = [cur.right, cur.left];
        queue.unshift(cur.left);
        queue.unshift(cur.right);
    }
    return root;
};

递归实现

var invertTree = function(root) {
    if(!root) return root;
    let tmp = root.left;
    root.left = root.right;
    root.right = tmp;
    invertTree(root.left);
    invertTree(root.right);
    return root;
};

二叉树中的列表

给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表。 如果在二叉树中,存在一条一直向下的路径,且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,那么请你返回 True ,否则返回 False 。 一直向下的路径的意思是:从树中某个节点开始,一直连续向下的路径。  

输入:head = [4,2,8], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
输出:true
解释:树中蓝色的节点构成了与链表对应的子路径。

来源: leetcode-cn.com/problems/li…

var isSubPath = function(head, root) {
    if(!root) return false;
    return dfs(head, root) || isSubPath(head, root.left) || isSubPath(head, root.right);
};

function dfs(head, root) {
    if(!head) return true;
    if(!root) return false;
    if(head.val !== root.val) return false;
    return dfs(head.next, root.left) || dfs(head.next, root.right);
}

输出二叉树

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

  • 行数 m 应当等于给定二叉树的高度。

  • 列数 n 应当总是奇数。

  • 根节点的值(以字符串格式给出)应当放在可放置的第一行正中间。根节点所在的行与列会将剩余空间划分为两部分(左下部分和右下部分)。你应该将左子树输出在左下部分,右子树输出在右下部分。左下和右下部分应当有相同的大小。即使一个子树为空而另一个非空,你不需要为空的子树输出任何东西,但仍需要为另一个子树留出足够的空间。然而,如果两个子树都为空则不需要为它们留出任何空间。

  • 每个未使用的空间应包含一个空的字符串""。

  • 使用相同的规则输出子树。

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

  来源: leetcode-cn.com/problems/pr…

var printTree = function(root) {
    let row = getDepth(root);
    let col = Math.pow(2, row) - 1;
    let arr = Array.from(new Array(row), () => new Array(col).fill(""));
    printSingleRow(arr, root, 0, 0, col);
    return arr;
};

function getDepth(root) {
    if(!root) return 0;
    let leftDepth = getDepth(root.left);
    let rightDepth = getDepth(root.right);
    return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}

function printSingleRow(arr, root, row, left, right) {
    if(root) {
        let mid = Math.floor((left + right) / 2);
        arr[row][mid] = String(root.val);
        printSingleRow(arr, root.left, row + 1, left, mid);
        printSingleRow(arr, root.right, row + 1, mid + 1, right);
    }
}

二叉树寻路

在一棵无限的二叉树上,每个节点都有两个子节点,树中的节点 逐行 依次按 “之” 字形进行标记。 如下图所示,在奇数行(即,第一行、第三行、第五行……)中,按从左到右的顺序进行标记; 而偶数行(即,第二行、第四行、第六行……)中,按从右到左的顺序进行标记。

给你树上某一个节点的标号 label,请你返回从根节点到该标号为 label 节点的路径,该路径是由途经的节点标号所组成的。

输入:label = 14
输出:[1,3,4,14]

来源: leetcode-cn.com/problems/pa…

var pathInZigZagTree = function(label) {
    let binary = label.toString(2);
    let count = binary.length - 1;
    let ans = new Array(count + 1);
    ans[count] = label;
    for (let i = ans.length - 2; i >= 0; i--) {
        let num = 1 << (count - 1);
        ans[i] = 3 * num - Math.floor(label / 2) - 1;
        label = ans[i];
        count--;
    }
    console.log(ans)
    return ans;
};