• 【LeetCode】二叉树的最近公共祖先


    二叉树的最近公共祖先

    链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

    题目大意:求p和q在root树上的公共祖先,p和q可以是自身的祖先

    方法1:

    归根结底还是分解成子问题,然后递归求解

    针对树上的每一个非pq节点X,pq节点的分布有以下四种情况:

    • 情况1:p和q在X的左右子树上,一边一个
    • 情况2:p和q都在X的左子树left上
    • 情况3:p和q都在X的右子树right上
    • 情况4:p和q既不在X的右子树上,也不在X的左子树上,在X的上面

    我们可以定义一个dfs(root,p,q) 返回p,q的最近公共祖先,针对以上四种情况,分别采取以下措施:

    • 针对情况1:既然p和q在X的左右子树上,那么X就是p和q的最近公共祖先
    • 针对情况2:返回X的左子树left,left是p和q的最近公共祖先
    • 针对情况3:返回X的右子树right,right是p和q的最近公共祖先
    • 针对情况4:返回nil,因为p和q的最近公共子树还在X的上面,是X的祖先
    
    // 求解p,q在root中的最近公共祖先
    func dfs(root, p, q *TreeNode) *TreeNode {
    
    	// 自身也是自己的公共祖先
    	if root == nil || root == p || root == q {
    		return root
    	}
    
    	// dfs 从底部向上递归
    	left := dfs(root.Left, p, q)   // 求出p和q在root左子树上的最近公共组件
    	right := dfs(root.Right, p, q) //求出p和q在root右子树上的最近公共组件
    
    	// 判断 left和right的四种情况
    
    	if left == nil && right != nil { // p q分布在root右侧 right是p,q的最近公共祖先
    		return right
    	} else if right == nil && left != nil { // p q 分布在root左侧,left是p,q的最近公共祖先
    		return left
    	} else if right == nil && left == nil { // p q 不在root两侧,在root上面,返回nil
    		return nil
    	} else if right != nil && left != nil { // p q 分布在root两侧,说明root是最近公共祖先
    		return root
    	}
    
    	// 不会走到这里,上面那么写是为了更好理清思路
    	return root
    }
    func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
    	return dfs(root, p, q)
    }
    

    性能分析:

    时间复杂度:O(N)

    • 其中 NN 是二叉树的节点数。二叉树的所有节点有且只会被访问一次,从 p 和 q 节点往上跳经过的祖先节点个数不会超过 NN,因此总的时间复杂度为 O(N)

    空间复杂度:O(H)

    • 递归树深度,递归调用的栈深度取决于二叉树的高度,二叉树最坏情况下为一条链,此时高度为 N,因此空间复杂度为 O(N)

    方法2:

    用map记录每个节点的父节点

    遍历p的所有祖宗节点(通过记录了所有节点的父节点的map逐渐向上跳),包括自己,遍历过的就标记一下

    然后遍历q的所有祖宗节点,包括自己,遇到遍历过的标记节点,那么此节点就是pq的最近公共祖宗节点

    一直到遍历到根节点如果还没有遇到遍历过的标记节点,那么他们pq的最近公共祖先就是root

    var tmap map[*TreeNode]*TreeNode
    
    // 记录每个节点的父亲节点
    func dfs(root *TreeNode){
    	if root==nil{
    		return
    	}
    	if root.Left!=nil{
    		tmap[root.Left]=root
    		dfs(root.Left)
    	}
    	if root.Right!=nil{
    		tmap[root.Right]=root
    		dfs(root.Right)
    	}
    }
    func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
    	tmap=make(map[*TreeNode]*TreeNode)
    	dfs(root)
    
    	// 标记p的所有祖宗节点
    	flag:=make(map[*TreeNode]int)
    	curp:=p
    	for curp!=root{
    		flag[curp]=1
    		curp=tmap[curp]
    	}
    
    	// 遍历q的所有祖宗节点,遇到标记过的节点,那么该节点就是最近公共祖宗节点
    	curq:=q
    	for curq!=root{
    		if _,ok:=flag[curq];ok{
    			return curq
    		}else {
    			curq=tmap[curq]
    		}
    	}
    
    	// 特殊情况,root是他们的最近公共祖先节点
    	return root
    }
    

    性能分析:

    时间复杂度:O(N)

    空间复杂度:O(N)

  • 相关阅读:
    【软件工程Ⅱ】作业二 |分布式版本控制系统Git的安装与使用
    【软件工程Ⅱ】作业一 |走进计算机
    字符串、文件操作,英文词频统计预处理
    了解大数据的特点、来源与数据呈现方式
    作业五:结对项目-“四则运算”之升级版
    第四次作业:小学四则运算“软件”之初版
    阅读《构建之法》1-5章的感想
    作业二/Git的安装以及使用
    大三学习软件工程感想
    爬取全部的校园新闻
  • 原文地址:https://www.cnblogs.com/yinbiao/p/16106656.html
Copyright © 2020-2023  润新知