【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)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值