JS:二叉树(4)——刷题,最小深度,公共祖先,最大路径和

100. 相同的树 (简单)

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

因为遍历的函数有返回值,所以递归左右子树时,要用变量去接住。
!(p && q) 只要有一个为null ,表达式为true

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

代码属于先序遍历

103. 二叉树的锯齿形层序遍历 (中等)

给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

通过变量规定加入到列表的方向,实际只是对层次遍历的结果进修修改。对于对层次遍历练练手:

var zigzagLevelOrder = function(root) {
    if(root == null) return []
    let deque = [root], flag = true, ans=[]
    while(deque.length) {
        let temp = [], len = deque.length
        for (let i=1; i<=len; i++){
            let root = deque.shift()
            flag ? temp.push(root.val) : temp.unshift(root.val)
            root.left && deque.push(root.left)
            root.right && deque.push(root.right)
        } 
        flag = !flag
        ans.push(temp)
    }
    return ans;
};

107. 二叉树的层序遍历 II (中等)

给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

又是对层次遍历的结果进修修改,只改一行 ans.unshift(temp)

111. 二叉树的最小深度 (简单)

该题是BFS算法,就是层次遍历,记录每一层深度,遇到叶子节点就结束。
我用递归来完成,但需要避开导致错误的情形。

给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。

在这里插入图片描述
当没有左孩子,递归时返回0,但实际它没有左子树。
我的逻辑是,如果没有左子树,那么他返回的深度left就不能使用。右子树也一样操作。如果是叶子节点,他没有右子树和左子树,则left和right都不能用,所以直接返回1。

var minDepth = function(root) {
    if(root == null) return 0
    let left = minDepth(root.left)
    let right = minDepth(root.right)
    let min = null
    if(root.left) min = left
    if(root.right) {
        min = right
        if(root.left && left<right) min = left;
    }
    if(min==null) min = 0
    return min + 1
};

但其实有简洁方式,因为left和right虽然不能用,但他们的值为0。
在这里插入图片描述

层次遍历解法

var minDepth = function(root) {
    if(root == null) return 0
    let deque = [root], depth = 1
    while(deque.length){
        let len = deque.length
        for (let i=1; i<=len; i++){
            let root = deque.shift()
            if(root.left == null && root.right == null) return depth
            root.left && deque.push(root.left)
            root.right && deque.push(root.right)
        }
        depth++
    }
    return depth;
};

222. 完全二叉树的节点个数 (中等) fail

更高效的方法

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

完全二叉树比普通二叉树特殊,但又没有满二叉树那么特殊,计算它的节点总数,可以说是普通二叉树和完全二叉树的结合版

文章直接上答案了,它不用考虑root == null,因为叶子节点时,它的节点数就是math.pow(2,1)- 0 = 1

var countNodes = function(root) {
    let l = r = root, hl = hr = 0;
    while(l){
        l=l.left;
        hl++;
    }
    while(r){
        r=r.right;
        hr++;
    }
    if(hl==hr) return Math.pow(2, hl) - 1;
    return 1 + countNodes(root.left) + countNodes(root.right);
};

后面才说明一下,这个算法的时间复杂度是 O(logN*logN):
一棵完全二叉树的两棵子树,至少有一棵是满二叉树:.
在这里插入图片描述

236. 二叉树的最近公共祖先 (中等)fail

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

最近公共祖先(Lowest Common Ancestor,简称 LCA)

这道题也在剑指 Offer 68为简单题 当时用的数组记录两条搜索路径。

遇到任何递归型的问题,无非就是灵魂三问:
1、这个函数是干嘛的?root是不固定的
在这里插入图片描述

2、这个函数参数中的变量是什么的是什么?

3、得到函数的递归结果,你应该干什么?
在这里插入图片描述
在这里插入图片描述

可以试着写出递归代码

var lowestCommonAncestor = function(root, p, q) {
    if (root == null) return null
    if (root == p || root == q) return root // q为p的子节点时,到最后就只找到p,但p就是答案
    let left = lowestCommonAncestor(root.left, p, q)
    let right = lowestCommonAncestor(root.right, p, q)
    if(left==null && right==null) return null
    if(left && right) return root
    return left ? left : right // 好比在一边找到目标节点,拿着目标节点要向上通报
};

在这里插入图片描述

124. 二叉树中的最大路径和 (困难)

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。

在这里插入图片描述
启发我的是:一个节点有三个方向,因为路径,我们只能三选二。
观察当 b 作为根节点root ,不考虑它的父亲,实际我们递归时也无法得到。所以就只能考虑 d - b - e 的总和。对于b肯定舍弃小于0的子树。当b作为根节点结束时,他要向上层汇报,如果他是负数,也要如实汇报,至于他的父亲会自己判断(针对所有节点都是负值)。这时,面临三选二情况,对于b就是二选一。
在这里插入图片描述

let maxsum = null
function maxGain(root) {
    if(root == null) return 0
    let leftsum = Math.max(maxGain(root.left), 0)
    let rightsum = Math.max(maxGain(root.right), 0)
    let cursum = leftsum + rightsum + root.val
    if(cursum > maxsum) maxsum = cursum
    let construction = root.val + (leftsum > rightsum ? leftsum : rightsum)
    return construction
}
var maxPathSum = function(root) {
    maxsum = Number.MIN_SAFE_INTEGER
    maxGain(root)
    return maxsum
};

515. 在每个树行中找最大值 (中等)

给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。

层次遍历

var largestValues = function(root) {
    if(root==null) return []
    let deque = [root], ans = []
    while(deque.length) {
        let len = deque.length, tmp = Number.MIN_SAFE_INTEGER;
        for (let i=1; i<=len; i++){
            let node = deque.shift()
            if(node.val > tmp) tmp = node.val
            node.left && deque.push(node.left)
            node.right && deque.push(node.right)
        }
        ans.push(tmp)
    }
    return ans;
}; 

965. 单值二叉树 (简单)

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

需要全局变量提取结束遍历

var isUnivalTree = function(root) {
    if (root == null) return true
    let tar = root.val, conf = true
    function traverse(root) {
        if(root==null) return
        if(root.val!=tar) conf = false
        if(conf){
            traverse(root.left)
            traverse(root.right)
        }
    }
    traverse(root)
    return conf
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值