给定一棵树,同时给出树中的两个结点(n1和n2),求它们的最低公共祖先。也就是常见的LCA(Lowest Common Ancestor )问题。
看下面的图就明白了:
![lca](http://d2o58evtke57tz.cloudfront.net/wp-content/uploads/lca.png)
方法一
下面是一个简单的复杂度为 O(n) 的算法,解决LCA问题
1) 找到从根到n1的路径,并存储在一个向量或数组中。
2)找到从根到n2的路径,并存储在一个向量或数组中。
3) 遍历这两条路径,直到遇到一个不同的节点,则前面的那个即为最低公共祖先.
下面的C++的程序实现
10 | struct Node *left, *right; |
15 | Node *temp = new Node; |
17 | temp->left = temp->right = NULL; |
21 | bool findpath(Node * root,vector< int > &path, int key){ |
22 | if (root == NULL) return false ; |
23 | path.push_back(root->key); |
24 | if (root->key == key) return true ; |
26 | bool find = ( findpath(root->left, path, key) || findpath(root->right,path ,key) ); |
33 | int findLCA(Node * root, int key1, int key2){ |
34 | vector< int > path1,path2; |
35 | bool find1 = findpath(root, path1, key1); |
36 | bool find2 = findpath(root, path2, key2); |
39 | for ( int i=0; i<path1.size(); i++){ |
40 | if (path1[i] != path2[i]){ |
54 | Node * root = newNode(1); |
55 | root->left = newNode(2); |
56 | root->right = newNode(3); |
57 | root->left->left = newNode(4); |
58 | root->left->right = newNode(5); |
59 | root->right->left = newNode(6); |
60 | root->right->right = newNode(7); |
61 | cout << "LCA(4, 5) = " << findLCA(root, 4, 5); |
62 | cout << "\nLCA(4, 6) = " << findLCA(root, 4, 6); |
63 | cout << "\nLCA(3, 4) = " << findLCA(root, 3, 4); |
64 | cout << "\nLCA(2, 4) = " << findLCA(root, 2, 4); |
输出:
时间复杂度: O(n), 树被遍历了两次,每次遍历复杂度不超过n,然后比较路径。
第二种方法(只遍历一次)
上面的方法虽然是O(n),但是操作依然繁琐了一点,并且需要额外的空间来存储路径。其实可以只遍历一次,利用递归的巧妙之处。学好二叉树,其实就是学好递归。
从root开始遍历,如果n1和n2中的任一个和root匹配,那么root就是LCA。 如果都不匹配,则分别递归左、右子树,如果有一个 key(n1或n2)出现在左子树,并且另一个key(n1或n2)出现在右子树,则root就是LCA. 如果两个key都出现在左子树,则说明LCA在左子树中,否则在右子树。
06 | struct Node *left, *right; |
09 | Node* newNode( int key) |
11 | Node *temp = new Node; |
13 | temp->left = temp->right = NULL; |
19 | struct Node *findLCA( struct Node* root, int n1, int n2) |
21 | if (root == NULL) return NULL; |
25 | if (root->key == n1 || root->key == n2) |
28 | Node *left_lca = findLCA(root->left, n1, n2); |
29 | Node *right_lca = findLCA(root->right, n1, n2); |
31 | if (left_lca && right_lca) return root; |
33 | return (left_lca != NULL)? left_lca: right_lca; |
40 | Node * root = newNode(1); |
41 | root->left = newNode(2); |
42 | root->right = newNode(3); |
43 | root->left->left = newNode(4); |
44 | root->left->right = newNode(5); |
45 | root->right->left = newNode(6); |
46 | root->right->right = newNode(7); |
47 | cout << "LCA(4, 5) = " << findLCA(root, 4, 5)->key; |
48 | cout << "\nLCA(4, 6) = " << findLCA(root, 4, 6)->key; |
49 | cout << "\nLCA(3, 4) = " << findLCA(root, 3, 4)->key; |
50 | cout << "\nLCA(2, 4) = " << findLCA(root, 2, 4)->key; |
时间复杂度为O(n),但是上面的方法还是有所局限的,必须保证两个要查找的节点n1和n2都出现在树中。如果n1不在树中,则会返回n2为LCA,理想答案应该为NULL。要解决这个问题,可以先查找下 n1和n2是否出现在树中,然后加几个判断即可。
原文:http://www.acmerblog.com/lca-lowest-common-ancestor-5574.html