剑指offer经典题目
Question
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
Snipaste_2020-05-10_12-24-44.png
示例 1:
| 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 输出: 3 解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
|
示例 2:
| 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 输出: 5 解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
|
说明:所有节点的值都是唯一的。p、q 为不同节点且均存在于给定的二叉树中。
Intuition
如果有指向父节点的指针,直接一路回溯就能得到root到node的一条链表,两个node就能得到两条链表,找两条链表上最近的公共系节点,就是最近公共祖先。
如果没有指向父节点的指针,从root开始一路深度优先遍历,拿到root到node的一条链表,两个node就能得到两条链表,找两条链表上最近的公共系节点,就是最近公共祖先。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
| import unittest from typing import List
class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None def __str__(self): return str(self.val)
class BinaryTree():
def Create(self, items): if len(items) == 0: return None nodeQueue = [] root = TreeNode(items[0]) nodeQueue.append(root) cur = None lineNodeNum = 2 startIndex = 1 restLength = len(items) - 1 while restLength > 0: i = startIndex while i < startIndex + lineNodeNum: if i == len(items): return root cur = nodeQueue.pop(0) if items[i] != None: cur.left = TreeNode(items[i]) nodeQueue.append(cur.left)
if i + 1 == len(items): return root if items[i + 1] != None: cur.right = TreeNode(items[i + 1]) nodeQueue.append(cur.right) i += 2 startIndex += lineNodeNum restLength -= lineNodeNum lineNodeNum = len(nodeQueue) * 2 return root
def PreOrder(self, root): """ Pre-Order Traversal of binary tree :param root: root node of target binary tree :return: TreeNode """ if not root: return [None] res = [root.val] left_tree = self.PreOrder(root.left) right_tree = self.PreOrder(root.right) return res + left_tree + right_tree
class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': if root == None: return None res = [] def _dfs(path=[], node=None, value=None): if node.val == value: res.append(path[:] + [node]) return True if node.left: if _dfs(path + [node], node.left, value): return True if node.right: if _dfs(path + [node], node.right, value): return True return False
value = p.val _dfs([], root, value) value = q.val _dfs([], root, value) line_1 = res[0] line_2 = res[1]
count = 0 public_node = None while count < len(line_1) and count < len(line_2): if line_1[count].val == line_2[count].val: public_node = line_1[count] count += 1 else: break
return public_node
class TestSolution(unittest.TestCase): def setUp(self): self.test_class = Solution()
def test_solution(self): roots = [ [3, 5, 1, 6, 2, 0, 8, None, None, 7, 4], [3, 5, 1, 6, 2, 0, 8, None, None, 7, 4], [] ] p = [5, 5, 6] q = [1, 4, 1] answers = [3, 5, None] res = [] bTree = BinaryTree() for i in range(len(roots)): root = bTree.Create(roots[i]) _p = TreeNode(p[i]) _q = TreeNode(q[i]) res_node = self.test_class.lowestCommonAncestor(root, _p, _q) if res_node: res.append(res_node.val) else: res.append(None) self.assertEqual(res, answers) def tearDown(self): del self.test_class
if __name__ == "__main__": unittest.main()
|
Extension
Question
剑指offer 面试题68 - I. 二叉搜索树的最近公共祖先(易) & LeetCode 235. 二叉搜索树的最近公共祖先(易)
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” 例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
binarysearchtree_improved
示例 1: | 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 输出: 6
|
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2: | 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4 输出: 2
|
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
说明:所有节点的值都是唯一的。p、q 为不同节点且均存在于给定的二叉搜索树中。
Intuition
二又搜索树是排序过的,位于左子树的节点都比父节点小,而位于右子树的节点都比父节点大,我们只需要从树的根节点开始和两个输入的节点进行比较。如果当前节点的值比两个节点的值都大,那么最低的共同父节点一定在当前节点的左子树中,于是下一步遍历当前节点的左子节点。如果当前节点的值比两个节点的值都小,那么最低的共同父节点一定在当前节点的右子树中,于是下一步遍历当前节点的右子节点。这样,在树中从上到下找到的第一个在两个输入节点的值之间的节点就是最低的公共祖先。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None
class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': self.res = None def _dfs(path=[], node=None): if samll <= node.val <= big: self.res = node return True
if node.val >= samll and node.left: if _dfs(path + [node], node.left): return True
if node.val <= big or node.right: if _dfs(path + [node], node.right): return True return False big = p.val if p.val > q.val else q.val samll = p.val if p.val < q.val else q.val _dfs([], root)
return self.res
|