树的最低公共祖先和主方法

LCA 分为几种情况

1. Binary Search Tree 的 LCA

2. Binary Tree with parent pointer

3. Binary Tree without parent pointer

4. ordinary Tree without parent pointer

 

Binary Search Tree

思路

1. 对一个节点 node, 假如 n1,n2 均大/小于 node->val, 那么LCA一定在 node->right 分支上

2. 若一个大于一个小于, 则 LCA 就是 node 本身

3. updown 解法, log(n) 的时间复杂度

 

Binary Tree with Parent Pointer

思路

1. 先 find 到两个节点, 并记录深度

2. 较深的节点先走深度之差步, 然后两个节点同时向上走,直到相遇

3. 上面几乎就是求解链表的公共节点问题了, 时间复杂度为 o(n), 空间复杂度为常数

 

Binary Tree without Parent Pointer

Top-Down 解法 worst case, 时间复杂度为 o(n^2)

1. 从 root 开始, 对每一个节点 node 作检测, 若 node 等于两个节点之一, 而返回 node

2. 否则, 检测 node->left 中, 有几个待求 LCA 节点存在(0?1?2?), 若 0,2 则直接到另一侧子树中去寻找

3. 若为 1, 则说明两个节点在 node 两侧, 返回 node 即可

在树是平衡树时, 每次减半, 时间复杂度计算方法如下:

时间复杂度的计算方法

T(n) = T(n/2) + n/2

a = 1, b = 2, f(n) = n

根据主定理, 时间复杂度为 o(n*logn)

若树是 degenerate 的, 时间复杂度为 o(n^2)

 

Bottom-up 解法 时间复杂度为 o(n)

上面解法的问题在于重复计算, 而 bottom-up 则致力于解决重复计算

从下到上遍历, 一旦遇到两个节点中的一个, 则将该节点传递到其父亲, 直到两个节点相遇, 返回即可

思路有些抽象, 代码却很简洁

 

Update 2014年2月24日12:00:56

这实际上就是后序遍历, root 访问之前, 其左右孩子都已被访问过了. 代码的框架和求 depth 一致

 

Node *LCA(Node *root, Node *p, Node *q) {
  if (!root) return NULL;
  if (root == p || root == q) return root;
  Node *L = LCA(root->left, p, q);
  Node *R = LCA(root->right, p, q);
  if (L && R) return root;  // if p and q are on both sides
  return L ? L : R;  // either one of p,q is on one side OR p,q is not in L&R subtrees
}

Leetcode 上有完整的描述 

 

一般树的 LCA

使用 dfs 定位两个节点的位置, 并保存路径, 然后求两个链表的公共祖先

 

主方法

为如下形式的递归式提供了一种“菜谱”式的求解方法,如下所示

其中a≥1和b>1是常数,f(n)是渐近正函数。为了使用主方法,需要牢记三种情况,但随后你就可以很容易地求解很多递归式,通常不需要纸和笔的帮助。

  主方法依赖于下面的定理。

  定理4.1(主定理)    令a≥1和b>1是常数,f(n)是一个函数,T(n)是定义在非负整数上的递归式:

  那么T(n)有如下渐近界:

  

主定理不能适合于这样的递归式:T(n)=2T(n/2)+nlgn,因为该递归式落入了情况2和情况3之间的间隙。利用主定理计算递归式非常方便,不用再画递归树了。

posted @ 2014-01-17 21:25  SangS  阅读(581)  评论(0编辑  收藏  举报