树(更新至11.17)

公共祖先

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

其实吧,这题我用一种偏复杂的方法做了。
我的方法:每次递归返回两个值:①有无结点1?②有无结点2?第一次两边都有的就是结果。

题解的方法:每次递归只返回一个值:是否存在结点1或者结点2。然后分几种情况讨论:
①两边都没有:返回None
②有一边没有:返回另一边
③两边都有:返回root

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root is None: return
        if root.val == p.val or root.val == q.val: return root
        a = self.lowestCommonAncestor(root.left, p, q)
        b = self.lowestCommonAncestor(root.right, p, q)
        if a and b: return root
        if a is None: return b
        if b is None: return a

说几个要点
1.前置增加判断条件:若碰到或者q,直接停止递归往上回溯,因为公共祖先一定不可能在下面,已经没有往下走的必要了。(这里是先序遍历
2.后面的判断是后序遍历,请注意a和b的值是一直传递上去的,而并不是重新定义。也就是说,如果底部某一个结点遍历到了p的值,会一直顺着回溯方向往上传递

先普及一下树的概念。
树的高度:从叶子结点开始计算——后序遍历
树的深度:从根节点从0开始计算——先序遍历或者 直接在递归函数参数上进行一次+1

传染树

给你一棵二叉树的根节点 root ,二叉树中节点的值 互不相同 。另给你一个整数 start 。在第 0 分钟,感染 将会从值为 start 的节点开始爆发。

每分钟,如果节点满足以下全部条件,就会被感染:

节点此前还没有感染。
节点与一个已感染节点相邻。
返回感染整棵树需要的分钟数。
在这里插入图片描述
法一:
考虑情况。
①感染结点就是当前root——求的是向下的距离,也就是感染结点的高度 - 1
②当前遍历root的左子树有感染结点——求的是向上的距离,= 右子树高度 + (感染结点深度 - 当前深度)
③右子树有感染结点——如法炮制
④两边都没有——不用计算,但就算计算了也不可能是最大值,也可以不把他当成一种特殊情况。

class Solution:
    def __init__(self):
            self.start_depth = -1 #储存感染结点的深度,初始值为-1
            self.start = 3
            self.res = 0
    def order(self, root, depth):
            if (root == None):
                return 0
            l = self.order(root.left, depth + 1)
            inleft = self.start_depth != -1
            r = self.order(root.right, depth + 1)
            inright = self.start_depth != -1
            if (root.val == self.start):
                self.start_depth = depth
                self.res = max(self.res, max(l, r))
            if(inleft):
                self.res = max(self.res, r + self.start_depth - depth)
            elif(inright):
                self.res = max(self.res, l + self.start_depth - depth)
            return max(l, r) + 1
    def amountOfTime(self, root: Optional[TreeNode], start: int) -> int:
        self.start = start
        self.order(root, 0)
        return self.res

说一下重要的点:
为什么要设置深度和高度两个参数?不能都用一个来计算吗?
当然不能。当遍历到某个节点出现情况②/③时,你并不能拿到到最底层叶子结点的深度,所以情况②的右子树只能用高度求。
情况②的左子树只能用深度差求,如果用高度差求,你不能保证感染结点在你最长的那条路径下面。考虑一个例子:[1,2,3,4,5(感染),null, null, 6],遍历到1的时候,不能用高度差求左子树的和,画个图就知道了。

法二
法二用到了很清新的思路:将树转换为图!这个思路太好理解了,转换为图之后以感染点作为起点,计算时间即可!

class Solution:
    def amountOfTime(self, root: Optional[TreeNode], start: int) -> int:
        
        graph = collections.defaultdict(list)   # 无向图
        
        # 一次BFS:层次遍历 + 建图
        deque = collections.deque([root])
        while deque:
            for _ in range(len(deque)):
                node = deque.popleft()
                if node.left:
                    deque.append(node.left)
                    graph[node.val].append(node.left.val)
                    graph[node.left.val].append(node.val)
                if node.right:
                    deque.append(node.right)
                    graph[node.val].append(node.right.val)
                    graph[node.right.val].append(node.val)
        
        # 二次BFS:模拟感染(SI疾病传播模型)
        deque = collections.deque([start])
        visited = set([start])
        time = -1
        while deque:
            for _ in range(len(deque)):
                node = deque.popleft()
                for neighbor in graph[node]:
                    if neighbor not in visited:
                        visited.add(neighbor)
                        deque.append(neighbor)
            time += 1
        
        return time

所有距离为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 的结点,值分别为 7,4,以及 1

这题用到了树变图的方法!由于树的结点值都不相同,因此可以采取构建图的方法,然后利用图找距离为2的结点,就easy了!

const int max_size = 510;
class Solution {
public:
    vector<int> distanceK(TreeNode* root, TreeNode* target, int k) {
        vector<vector<int>> graph(max_size);
        vector<int> visited(max_size, 0);
        buildgraph(root, graph);
        res.clear();
        visited[target->val] = 1;
        dfs(graph, target->val, k, visited);
        return res;
    }
    void buildgraph(TreeNode* root, vector<vector<int>>& graph){
        if(!root) return;
        if(root->left){
            graph[root->val].push_back(root->left->val);
            graph[root->left->val].push_back(root->val);
            buildgraph(root->left, graph);
        }
        if(root->right){
            graph[root->val].push_back(root->right->val);
            graph[root->right->val].push_back(root->val);
            buildgraph(root->right, graph);
        }
        return;
    }
    void dfs(vector<vector<int>> &graph, int val, int k, vector<int>& visited){
        if(k == 0){
            res.push_back(val);
            return;
        }
        for (int i = 0; i < graph[val].size(); ++i){
            int neighboor = graph[val][i];
            if (visited[neighboor] == 0){
                visited[neighboor] = 1;
                dfs(graph, neighboor, k - 1, visited);
            }
        }
        return;
    }
private:
    vector<int> res;
};

下面附上重点:
①图是无向图,邻接矩阵存储,由于题目有给出结点数范围1-500,所以创建了一个510*510的邻接表。构件图用先序,不谈了。
②函数的形参(特别是visited)要用到&,否则递归的visited值不会同步更新,会产生重复。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值