我的答案是:分别遍历这两个节点的树,直到到达期望的节点。在遍历时,同时将元素和下一个地址存储在链表中。然后我们就有了两个链表。因此,尝试比较这两个链表,两个链表中最后一个相同的节点就是父节点。
我认为这个解决方案是正确的,如果我错了,请纠正我。如果这个解决方案是正确的,那么请问是否有比这更好的解决方案?
我认为你可以同时搜索两个节点,当搜索分叉的时候,这个分叉点就是它们的共同祖先。
commonAncestor tree a b:
value := <value of node 'tree'>
if (a < value) && (b < value)
then commonAncestor (left tree) a b
else if (a > value) && (b > value)
then commonAncestor (right tree) a b
else tree
tree
的左侧等)。编辑1:
以下是大纲
struct _node {
my_type data;
struct _node *left;
struct _node *right;
}
q = queue_create ();
queue_insert (q, head);
temp = head;
while (!empty (q))
{
temp = queue_remove (q);
if (
(temp->left == my_random_node_1) && (head->right == my_random_node_2) ||
(temp->left == my_random_node_2) && (head->right == my_random_node_1)
)
{
/* temp is the common parent of the two target notes */
/* Do stuffs you need to do */
}
/* Enqueue the childs, so that in successive iterations we can
* check them, by taking out from the queue
*/
push (q, temp->left);
push (q, temp->right);
}
更新
之前的算法只能找到共同的父节点(直接祖先),因此如果随机选择的两个节点不是公共父节点的子节点,则无法找到答案。
下面的算法将找到共同的祖先,而不仅仅是父节点。
我认为以下算法可以解决问题:
进行二叉树的后序遍历,并查找随机节点1 r1
,如果找到则将其标记为状态一,并继续查找第二个节点,如果找到则更新状态变量为状态二,停止搜索并返回。状态变量应由每个节点传递给其父节点(递归)。第一个遇到状态二的节点就是公共祖先。
算法的实现如下:
int postorder (node *p, int r1, int r2)
{
int x = 0; /* The state variable */
if (p->data == TERMINAL_VAL)
return x;
/* 0x01 | 0x02 = 0x03 threfore
* state one is when x = 0x01 or x = 0x02
* state two is when x = 0x03
*/
if (p->data == r1)
x |= 0x01;
else if (p->data == r2)
x |= 0x02;
/* if we have x in state two, no need to search more
*/
if (x != 0x03)
x |= postorder (p->left, r1, r2);
if (x != 0x03)
x |= postorder (p->right, r1, r2);
/* In this node we are in state two, print node if this node
* is not any of the two nodes r1 and r2. This makes sure that
* is one random node is an ancestor of another random node
* then it will not be printed instead its parent will be printed
*/
if ((x == 0x03) && (p->data != r1) && (p->data != r2))
{
printf ("[%c] ", p->data);
/* set state variable to 0 if we do not want to print
* the ancestors of the first ancestor
*/
x = 0;
}
/* return state variable to parent
*/
return x;
}
main
中返回0x03
,可以检测到。以上所述的方法行不通,因为您假设这两个节点都是某个特定节点的直接子节点...
8
10 12
7
我给出的节点是7和12,答案必须是8。我们这样做吧。
find(root, d1, d2, n1=null, n2=null)
{
if(n1 && n2) return;
if(!root) return;
else if(root -> d == d1 ) n1 = root;
else if(root -> d == d2 ) n2 = root;
find(root->left, d1, d2, n1, n2);
find(root->right, d1, d2, n1, n2);
}
LCA(root, d1, d2)
{
node *n1=null, *n2=null;
find(root, d1, d2, n1, n2);
if(n1 == null || n2 == null )error 'nodes not present' exit(0);
findIntersect(n1, n2);
}
findInterSect(node *n1, node *n2)
{
l1 = length(n1);
l2 = length(n2);
node *g = n2, *l = n1;
diff = abs(l1 - l2);
if(l1>l2) g = n1 l =n2
while(diff) g = g->parent; diff--;
// now both nodes are at same level
while(g != l) g= g->parent, l = l->parent;
}
伪代码:
node *FindCommonAncestor(node *root, node *node1, node *node2) {
node *current = node1;
node_list temp_list;
temp_list.add(current);
while (current != root) {
current = current.parent;
temp_list.add(current);
}
current = node2;
while (current not in temp_list) {
current = current.parent;
}
return current;
}
由于我们事先不知道n和m的值,因此让我们用树中节点的总数S来表示。如果树是平衡的,则n和m都受到log_2(S)的限制,因此运行时间为O(log_2(S)^2)。Log_2非常强大,因此S必须变得非常大,我才会担心2的幂次方。如果树不平衡,则失去了log_2(树可能实际上退化为链表)。因此,最坏情况(当一个节点是完全退化树的根,另一个节点是叶子)的时间复杂度为O(S^2)。
你好,这将返回树的根和传递给节点的val1、val2数据值的最低祖先节点值。
int CommonAncestor(node *root, int val1,int val2)
{
if(root == NULL || (! root->left && ! root->right )
return false;
while(root)
{
if(root->data < val1 && root->data < val2)
{
root = root->left;
}
else if(root->data > val1 && root->data > val2)
{
root= root->right;
}
else
return root->data;
}
}
以下是两种在c# (.net)中的方法(上面都有讨论)供参考:
在二叉树中找到LCA的递归版本(O(N) - 最多每个节点都会被访问) 解决方案的主要点是LCA是(a) 二叉树中仅有的一个节点,其中两个元素分别位于子树(左侧和右侧)的一侧。 (b) 并且无论哪个节点位于哪一侧都没有关系 - 最初我尝试保留这些信息,但显然递归函数变得非常混乱。一旦我意识到这一点,它就变得非常优雅。
搜索两个节点(O(N)),并跟踪路径(使用额外空间 - 因此,如果二叉树平衡良好,则#1可能更优,即使空间可能可以忽略不计,因为额外的内存消耗将只是O(log(N)))。 因此,路径被比较(基本上类似于接受的答案 - 但路径是通过假定指针节点不存在于二叉树节点中来计算的)
仅为完整性而提供(与问题无关),BST中的LCA(O(log(N))
测试
递归:
private BinaryTreeNode LeastCommonAncestorUsingRecursion(BinaryTreeNode treeNode,
int e1, int e2)
{
Debug.Assert(e1 != e2);
if(treeNode == null)
{
return null;
}
if((treeNode.Element == e1)
|| (treeNode.Element == e2))
{
//we don't care which element is present (e1 or e2), we just need to check
//if one of them is there
return treeNode;
}
var nLeft = this.LeastCommonAncestorUsingRecursion(treeNode.Left, e1, e2);
var nRight = this.LeastCommonAncestorUsingRecursion(treeNode.Right, e1, e2);
if(nLeft != null && nRight != null)
{
//note that this condition will be true only at least common ancestor
return treeNode;
}
else if(nLeft != null)
{
return nLeft;
}
else if(nRight != null)
{
return nRight;
}
return null;
}
上述私有递归版本是由以下公共方法调用的:
public BinaryTreeNode LeastCommonAncestorUsingRecursion(int e1, int e2)
{
var n = this.FindNode(this._root, e1);
if(null == n)
{
throw new Exception("Element not found: " + e1);
}
if (e1 == e2)
{
return n;
}
n = this.FindNode(this._root, e2);
if (null == n)
{
throw new Exception("Element not found: " + e2);
}
var node = this.LeastCommonAncestorUsingRecursion(this._root, e1, e2);
if (null == node)
{
throw new Exception(string.Format("Least common ancenstor not found for the given elements: {0},{1}", e1, e2));
}
return node;
}
通过跟踪两个节点的路径来解决:
public BinaryTreeNode LeastCommonAncestorUsingPaths(int e1, int e2)
{
var path1 = new List<BinaryTreeNode>();
var node1 = this.FindNodeAndPath(this._root, e1, path1);
if(node1 == null)
{
throw new Exception(string.Format("Element {0} is not found", e1));
}
if(e1 == e2)
{
return node1;
}
List<BinaryTreeNode> path2 = new List<BinaryTreeNode>();
var node2 = this.FindNodeAndPath(this._root, e2, path2);
if (node1 == null)
{
throw new Exception(string.Format("Element {0} is not found", e2));
}
BinaryTreeNode lca = null;
Debug.Assert(path1[0] == this._root);
Debug.Assert(path2[0] == this._root);
int i = 0;
while((i < path1.Count)
&& (i < path2.Count)
&& (path2[i] == path1[i]))
{
lca = path1[i];
i++;
}
Debug.Assert(null != lca);
return lca;
}
FindNodeAndPath的定义位置
private BinaryTreeNode FindNodeAndPath(BinaryTreeNode node, int e, List<BinaryTreeNode> path)
{
if(node == null)
{
return null;
}
if(node.Element == e)
{
path.Add(node);
return node;
}
var n = this.FindNodeAndPath(node.Left, e, path);
if(n == null)
{
n = this.FindNodeAndPath(node.Right, e, path);
}
if(n != null)
{
path.Insert(0, node);
return n;
}
return null;
}
public BinaryTreeNode BstLeastCommonAncestor(int e1, int e2)
{
//ensure both elements are there in the bst
var n1 = this.BstFind(e1, throwIfNotFound: true);
if(e1 == e2)
{
return n1;
}
this.BstFind(e2, throwIfNotFound: true);
BinaryTreeNode leastCommonAcncestor = this._root;
var iterativeNode = this._root;
while(iterativeNode != null)
{
if((iterativeNode.Element > e1 ) && (iterativeNode.Element > e2))
{
iterativeNode = iterativeNode.Left;
}
else if((iterativeNode.Element < e1) && (iterativeNode.Element < e2))
{
iterativeNode = iterativeNode.Right;
}
else
{
//i.e; either iterative node is equal to e1 or e2 or in between e1 and e2
return iterativeNode;
}
}
//control will never come here
return leastCommonAcncestor;
}
单元测试
[TestMethod]
public void LeastCommonAncestorTests()
{
int[] a = { 13, 2, 18, 1, 5, 17, 20, 3, 6, 16, 21, 4, 14, 15, 25, 22, 24 };
int[] b = { 13, 13, 13, 2, 13, 18, 13, 5, 13, 18, 13, 13, 14, 18, 25, 22};
BinarySearchTree bst = new BinarySearchTree();
foreach (int e in a)
{
bst.Add(e);
bst.Delete(e);
bst.Add(e);
}
for(int i = 0; i < b.Length; i++)
{
var n = bst.BstLeastCommonAncestor(a[i], a[i + 1]);
Assert.IsTrue(n.Element == b[i]);
var n1 = bst.LeastCommonAncestorUsingPaths(a[i], a[i + 1]);
Assert.IsTrue(n1.Element == b[i]);
Assert.IsTrue(n == n1);
var n2 = bst.LeastCommonAncestorUsingRecursion(a[i], a[i + 1]);
Assert.IsTrue(n2.Element == b[i]);
Assert.IsTrue(n2 == n1);
Assert.IsTrue(n2 == n);
}
}
前序遍历,直到遇到任何一个节点并保存到目前为止访问的节点。
中序遍历,在遇到任何一个(提供的两个节点之一)节点时开始保存节点,并保存列表直到下一个节点被遇到。
A
B C
D E F G
H I J K L M N O
假设 H 和 E 是两个随机节点。
找到在这三个序列中都出现的第一个节点...