这里的二叉树可能不一定是二进制搜索树 结构可以视为 -
struct node {
int data;
struct node *left;
struct node *right;
};
我可以与朋友一起解决的最大解决方案就是这样 -
考虑this binary tree:
inorder遍历产量 - 8,4,9,2,5,1,6,3,7
后序遍历产量 - 8,9,4,5,2,6,7,3,1
因此,例如,如果我们想要找到节点8和5的共同祖先,那么我们在inorder树遍历中列出所有8到5之间的节点,在这种情况下恰好是[ 4,9,2]。然后我们检查此列表中的哪个节点在后序遍历中最后出现,即为2.因此,8和5的共同祖先是2。
这个算法的复杂性,我相信是O(n)(对于顺序/后序遍历的O(n),其余的步骤再次是O(n),因为它们只不过是数组中的简单迭代)。但这很有可能是错误的。 : - )
但这是一种非常粗糙的方法,我不确定它是否会因某些情况而崩溃。这个问题还有其他(可能是更优的)解决方案吗?
答案 0 :(得分:105)
从root
节点开始向下移动,如果您发现任何节点有p
或q
作为其直接子节点,那么它就是LCA。 (编辑 - 如果p
或q
是节点的值,则应该返回。否则当p
或q
中的一个是其中的直接子项时,它将会失败其他。)
否则,如果您在其右侧(或左侧)子树中找到p
的节点,在其左侧(或右侧)子树中找到q
,则它就是LCA。
固定代码如下:
treeNodePtr findLCA(treeNodePtr root, treeNodePtr p, treeNodePtr q) {
// no root no LCA.
if(!root) {
return NULL;
}
// if either p or q is the root then root is LCA.
if(root==p || root==q) {
return root;
} else {
// get LCA of p and q in left subtree.
treeNodePtr l=findLCA(root->left , p , q);
// get LCA of p and q in right subtree.
treeNodePtr r=findLCA(root->right , p, q);
// if one of p or q is in leftsubtree and other is in right
// then root it the LCA.
if(l && r) {
return root;
}
// else if l is not null, l is LCA.
else if(l) {
return l;
} else {
return r;
}
}
}
如果以下代码是其他代码的直接子代,则以下代码会失败。
treeNodePtr findLCA(treeNodePtr root, treeNodePtr p, treeNodePtr q) {
// no root no LCA.
if(!root) {
return NULL;
}
// if either p or q is direct child of root then root is LCA.
if(root->left==p || root->left==q ||
root->right ==p || root->right ==q) {
return root;
} else {
// get LCA of p and q in left subtree.
treeNodePtr l=findLCA(root->left , p , q);
// get LCA of p and q in right subtree.
treeNodePtr r=findLCA(root->right , p, q);
// if one of p or q is in leftsubtree and other is in right
// then root it the LCA.
if(l && r) {
return root;
}
// else if l is not null, l is LCA.
else if(l) {
return l;
} else {
return r;
}
}
}
答案 1 :(得分:69)
Nick Johnson是正确的,如果你没有父指针,你可以做的最好的O(n)时间复杂度算法。)对于该算法的简单递归版本,请参阅Kinding's post中运行的代码在O(n)时间。
但请记住,如果您的节点有父指针,则可以使用改进的算法。对于这两个节点,通过从节点开始构造一个包含从根到节点的路径的列表,然后插入父节点。
所以对于你的例子中的8,你得到(显示步骤):{4},{2,4},{1,2,4}
对相关其他节点执行相同操作,导致(步骤未显示):{1,2}
现在比较你查找列表不同的第一个元素的两个列表,或者其中一个列表的最后一个元素,以先到者为准。
此算法需要O(h)时间,其中h是树的高度。在最坏的情况下,O(h)等价于O(n),但如果树是平衡的,那只是O(log(n))。它还需要O(h)空间。可以使用仅使用常量空间的改进版本,代码显示在CEGRD's post
中无论树是如何构造的,如果这是一个在树上执行多次而不在其间进行更改的操作,那么您可以使用其他需要O(n)[线性]时间准备的算法,但是然后找到任何一对只需要O(1)[常数]时间。有关这些算法的参考,请参阅Wikipedia上的最低共同祖先问题页面。 (感谢Jason最初发布此链接)
答案 2 :(得分:49)
这是JAVA中的工作代码
public static Node LCA(Node root, Node a, Node b) {
if (root == null) {
return null;
}
// If the root is one of a or b, then it is the LCA
if (root == a || root == b) {
return root;
}
Node left = LCA(root.left, a, b);
Node right = LCA(root.right, a, b);
// If both nodes lie in left or right then their LCA is in left or right,
// Otherwise root is their LCA
if (left != null && right != null) {
return root;
}
return (left != null) ? left : right;
}
答案 3 :(得分:27)
到目前为止给出的答案使用递归或存储,例如,内存中的路径。
如果你的树很深,这两种方法都可能会失败。
以下是我对这个问题的看法。 当我们检查两个节点的深度(距离根的距离)时,如果它们相等,那么我们可以安全地从两个节点向上移动到共同的祖先。如果其中一个深度更大,那么我们应该从更深的节点向上移动而留在另一个节点中。
以下是代码:
findLowestCommonAncestor(v,w):
depth_vv = depth(v);
depth_ww = depth(w);
vv = v;
ww = w;
while( depth_vv != depth_ww ) {
if ( depth_vv > depth_ww ) {
vv = parent(vv);
depth_vv--;
else {
ww = parent(ww);
depth_ww--;
}
}
while( vv != ww ) {
vv = parent(vv);
ww = parent(ww);
}
return vv;
该算法的时间复杂度为:O(n)。 该算法的空间复杂度为:O(1)。
关于深度的计算,我们首先要记住定义:如果v是root,则depth(v)= 0;否则,深度(v)=深度(父(v))+ 1.我们可以如下计算深度:
depth(v):
int d = 0;
vv = v;
while ( vv is not root ) {
vv = parent(vv);
d++;
}
return d;
答案 4 :(得分:7)
嗯,这取决于你的二叉树的结构。假设您有一些方法可以在给定树根的情况下找到所需的叶节点 - 只需将其应用于这两个值,直到您选择的分支发散为止。
如果你没有办法在给定根的情况下找到所需的叶子,那么你唯一的解决方案 - 无论是在正常操作中还是在找到最后一个公共节点 - 都是对树的强力搜索。
答案 5 :(得分:7)
可在以下网址找到: - http://goursaha.freeoda.com/DataStructure/LowestCommonAncestor.html
tree_node_type *LowestCommonAncestor(
tree_node_type *root , tree_node_type *p , tree_node_type *q)
{
tree_node_type *l , *r , *temp;
if(root==NULL)
{
return NULL;
}
if(root->left==p || root->left==q || root->right ==p || root->right ==q)
{
return root;
}
else
{
l=LowestCommonAncestor(root->left , p , q);
r=LowestCommonAncestor(root->right , p, q);
if(l!=NULL && r!=NULL)
{
return root;
}
else
{
temp = (l!=NULL)?l:r;
return temp;
}
}
}
答案 6 :(得分:5)
找出两个节点的共同祖先: -
这适用于二叉搜索树。
答案 7 :(得分:5)
Tarjan's off-line least common ancestors algorithm足够好了(参见Wikipedia)。 Wikipedia上的问题(最低的共同祖先问题)还有很多。
答案 8 :(得分:3)
我尝试用Java编写说明图片和工作代码,
http://tech.bragboy.com/2010/02/least-common-ancestor-without-using.html
答案 9 :(得分:2)
两个节点node1
和node2
之间的最低公共祖先是将两个节点都作为后代的树中的最低节点。
从根节点遍历二叉树,直到找到两个节点。每次访问节点时,都会将其添加到字典(称为parent
)中。
一旦在二叉树中找到两个节点,就使用字典获得node1
的祖先并将其添加到集合(称为ancestors
)中。
对于node2
,以相同的方式执行此步骤。如果node2
的祖先存在于node1
的祖先集中,则它是它们之间的第一个公共祖先。
以下是使用 stack 和 dictionary 实现的迭代python解决方案,其要点如下:
node1
和node2
将存在于二叉树中class Node:
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
def lowest_common_ancestor(root, node1, node2):
parent = {root: None}
stack = [root]
while node1 not in parent or node2 not in parent:
node = stack[-1]
stack.pop()
if node.left:
parent[node.left] = node
stack.append(node.left)
if node.right:
parent[node.right] = node
stack.append(node.right)
ancestors = set()
while node1:
ancestors.add(node1)
node1 = parent[node1]
while node2 not in ancestors:
node2 = parent[node2]
return node2.data
def main():
'''
Construct the below binary tree:
30
/ \
/ \
/ \
11 29
/ \ / \
8 12 25 14
'''
root = Node(30)
root.left = Node(11)
root.right = Node(29)
root.left.left = Node(8)
root.left.right = Node(12)
root.right.left = Node(25)
root.right.right = Node(14)
print(lowest_common_ancestor(root, root.left.left, root.left.right)) # 11
print(lowest_common_ancestor(root, root.left.left, root.left)) # 11
print(lowest_common_ancestor(root, root.left.left, root.right.right)) # 30
if __name__ == '__main__':
main()
这种方法的复杂性是:O(n)
答案 10 :(得分:2)
只要走到整个树的root
,只要两个给定的节点,比如说p
和q
,必须找到祖先,就在相同的子树(意味着它们的值都比较小或者都大于根)。
它直接从根部走向最不常见的祖先,而不是看着树的其余部分,所以它的速度和它一样快。有几种方法可以做到。
迭代,O(1)空间
<强>的Python 强>
def lowestCommonAncestor(self, root, p, q):
while (root.val - p.val) * (root.val - q.val) > 0:
root = (root.left, root.right)[p.val > root.val]
return root
<强>爪哇强>
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while ((root.val - p.val) * (root.val - q.val) > 0)
root = p.val < root.val ? root.left : root.right;
return root;
}
如果出现溢出,我会做(root.val - (long)p.val)*(root.val - (long)q.val)
递归
<强>的Python 强>
def lowestCommonAncestor(self, root, p, q):
next = p.val < root.val > q.val and root.left or \
p.val > root.val < q.val and root.right
return self.lowestCommonAncestor(next, p, q) if next else root
<强>爪哇强>
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return (root.val - p.val) * (root.val - q.val) < 1 ? root :
lowestCommonAncestor(p.val < root.val ? root.left : root.right, p, q);
}
答案 11 :(得分:2)
以下递归算法将在O(log N)中运行,以获得平衡二叉树。如果传入getLCA()函数的任何一个节点都与root相同,那么root将是LCA,并且不需要执行任何recussrion。
测试用例。 [1] 节点n1和&amp; n2在树中,位于父节点的两侧。 [2] 节点n1或n2是根,LCA是根。 [3] 树中只有n1或n2,LCA将是树根左子树的根节点,或者LCA将是树的右子树的根节点根
[4] 树中没有n1或n2,没有LCA。 [5] n1和n2都是彼此相邻的直线,LCA将是n1或n2,它们都是关闭树的根。
//find the search node below root
bool findNode(node* root, node* search)
{
//base case
if(root == NULL)
return false;
if(root->val == search->val)
return true;
//search for the node in the left and right subtrees, if found in either return true
return (findNode(root->left, search) || findNode(root->right, search));
}
//returns the LCA, n1 & n2 are the 2 nodes for which we are
//establishing the LCA for
node* getLCA(node* root, node* n1, node* n2)
{
//base case
if(root == NULL)
return NULL;
//If 1 of the nodes is the root then the root is the LCA
//no need to recurse.
if(n1 == root || n2 == root)
return root;
//check on which side of the root n1 and n2 reside
bool n1OnLeft = findNode(root->left, n1);
bool n2OnLeft = findNode(root->left, n2);
//n1 & n2 are on different sides of the root, so root is the LCA
if(n1OnLeft != n2OnLeft)
return root;
//if both n1 & n2 are on the left of the root traverse left sub tree only
//to find the node where n1 & n2 diverge otherwise traverse right subtree
if(n1OnLeft)
return getLCA(root->left, n1, n2);
else
return getLCA(root->right, n1, n2);
}
答案 12 :(得分:1)
在scala中,代码是:
abstract class Tree
case class Node(a:Int, left:Tree, right:Tree) extends Tree
case class Leaf(a:Int) extends Tree
def lca(tree:Tree, a:Int, b:Int):Tree = {
tree match {
case Node(ab,l,r) => {
if(ab==a || ab ==b) tree else {
val temp = lca(l,a,b);
val temp2 = lca(r,a,b);
if(temp!=null && temp2 !=null)
tree
else if (temp==null && temp2==null)
null
else if (temp==null) r else l
}
}
case Leaf(ab) => if(ab==a || ab ==b) tree else null
}
}
答案 13 :(得分:1)
考虑这个树
如果我们进行后序和前序遍历并找到第一个出现的共同前身和后继者,我们就会得到共同的祖先。
postorder =&gt; 0,2,1,5,4,6,3,8,10,11,9,14,15,13,12,7 preorder =&gt; 7,3,1,0,2,6,4,5,12,9,8,11,10,13,15,14
最少的共同祖先8,11
在后序中我们在8&amp;之后得到=&gt; 9,14,15,13,12,7。 11 在预购中,我们在8&amp;之前有=&gt; 7,3,1,0,2,6,4,5,12,9。 11
9是8&amp;之后出现的第一个常见数字。 11在后序和8&amp;之前11预购,因此9是答案
5,10的最少共同祖先
11,9,14,15,13,12,7后序 7,3,1,0,2,6,4预购
7是第一个在预订后5,10之后和之前5,10之前出现的数字,因此7是答案
答案 14 :(得分:1)
如果它是完整的二叉树,节点x的子节点为2 * x和2 * x + 1,那么有更快的方法可以做到这一点
int get_bits(unsigned int x) {
int high = 31;
int low = 0,mid;
while(high>=low) {
mid = (high+low)/2;
if(1<<mid==x)
return mid+1;
if(1<<mid<x) {
low = mid+1;
}
else {
high = mid-1;
}
}
if(1<<mid>x)
return mid;
return mid+1;
}
unsigned int Common_Ancestor(unsigned int x,unsigned int y) {
int xbits = get_bits(x);
int ybits = get_bits(y);
int diff,kbits;
unsigned int k;
if(xbits>ybits) {
diff = xbits-ybits;
x = x >> diff;
}
else if(xbits<ybits) {
diff = ybits-xbits;
y = y >> diff;
}
k = x^y;
kbits = get_bits(k);
return y>>kbits;
}
如何运作
- 获取代表x&amp;使用二进制搜索的y是O(log(32))
- x&amp;的二进制表示法的公共前缀y是共同的祖先
- 由较大的比特数表示的任何一个由k>&gt;带到相同的比特。 DIFF
- k = x ^ y擦除x&amp;的公共前缀ÿ
- 找到代表剩余后缀的位
- 通过后缀位移动x或y以获得共同前缀,这是共同的祖先。
醇>
这是有效的,因为基本上递增地将较大的数字除以2,直到两个数字相等。这个数字是共同的祖先。划分实际上是正确的转变操作。所以我们需要找到两个数字的公共前缀来找到最近的祖先
答案 15 :(得分:0)
虽然已经回答了这个问题,但这是我使用C编程语言解决这个问题的方法。尽管代码显示了二叉搜索树(就insert()而言),但该算法也适用于二叉树。我们的想法是在顺序遍历中遍历从节点A到节点B的所有节点,在后顺序遍历中查找这些节点的索引。在后序遍历中具有最大索引的节点是最低共同祖先。
这是一个有效的C代码,用于实现查找二叉树中最低共同祖先的函数。我也提供所有实用程序功能等,但跳转到CommonAncestor()以便快速理解。
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <math.h>
static inline int min (int a, int b)
{
return ((a < b) ? a : b);
}
static inline int max (int a, int b)
{
return ((a > b) ? a : b);
}
typedef struct node_ {
int value;
struct node_ * left;
struct node_ * right;
} node;
#define MAX 12
int IN_ORDER[MAX] = {0};
int POST_ORDER[MAX] = {0};
createNode(int value)
{
node * temp_node = (node *)malloc(sizeof(node));
temp_node->left = temp_node->right = NULL;
temp_node->value = value;
return temp_node;
}
node *
insert(node * root, int value)
{
if (!root) {
return createNode(value);
}
if (root->value > value) {
root->left = insert(root->left, value);
} else {
root->right = insert(root->right, value);
}
return root;
}
/* Builds inorder traversal path in the IN array */
void
inorder(node * root, int * IN)
{
static int i = 0;
if (!root) return;
inorder(root->left, IN);
IN[i] = root->value;
i++;
inorder(root->right, IN);
}
/* Builds post traversal path in the POST array */
void
postorder (node * root, int * POST)
{
static int i = 0;
if (!root) return;
postorder(root->left, POST);
postorder(root->right, POST);
POST[i] = root->value;
i++;
}
int
findIndex(int * A, int value)
{
int i = 0;
for(i = 0; i< MAX; i++) {
if(A[i] == value) return i;
}
}
int
CommonAncestor(int val1, int val2)
{
int in_val1, in_val2;
int post_val1, post_val2;
int j=0, i = 0; int max_index = -1;
in_val1 = findIndex(IN_ORDER, val1);
in_val2 = findIndex(IN_ORDER, val2);
post_val1 = findIndex(POST_ORDER, val1);
post_val2 = findIndex(POST_ORDER, val2);
for (i = min(in_val1, in_val2); i<= max(in_val1, in_val2); i++) {
for(j = 0; j < MAX; j++) {
if (IN_ORDER[i] == POST_ORDER[j]) {
if (j > max_index) {
max_index = j;
}
}
}
}
printf("\ncommon ancestor of %d and %d is %d\n", val1, val2, POST_ORDER[max_index]);
return max_index;
}
int main()
{
node * root = NULL;
/* Build a tree with following values */
//40, 20, 10, 30, 5, 15, 25, 35, 1, 80, 60, 100
root = insert(root, 40);
insert(root, 20);
insert(root, 10);
insert(root, 30);
insert(root, 5);
insert(root, 15);
insert(root, 25);
insert(root, 35);
insert(root, 1);
insert(root, 80);
insert(root, 60);
insert(root, 100);
/* Get IN_ORDER traversal in the array */
inorder(root, IN_ORDER);
/* Get post order traversal in the array */
postorder(root, POST_ORDER);
CommonAncestor(1, 100);
}
答案 16 :(得分:0)
以下是到达solve this的最快方法。空间复杂度O(1),时间复杂度O(n)。如果
如果一个节点的left和right值都不为null,则该节点为 您的答案(第三个“如果”从顶部开始)。在迭代是否找到值时,如 所有的值都是唯一的,必须存在,我们不需要 搜索其后代。因此,只需返回找到的匹配节点即可。如果 节点的左右分支内容null传播null 向上。到达顶级递归时,如果返回一个分支 价值,当其他两个都返回时,其他人就不会继续传播该价值 不为null返回当前节点。如果达到根级别递归 一个null,另一个不为null,返回不为null的值,作为问题, 承诺值存在,它必须在 找到了我们从未遍历的节点。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null;
if(root.val == p.val || root.val == q.val) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right !=null) return root;
if (left == null && right ==null) return null;
return (left != null ? left : right);
}
}
答案 17 :(得分:0)
解决方案1:递归-更快
- 时间复杂度:O(n)
- 空间复杂度:O(h)-用于递归调用堆栈
class Solution
{
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
{
if(root == null || root == p || root == q)
return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null)
return right;
else if(right == null)
return left;
else
return root; // If(left != null && right != null)
}
}
解决方案2:迭代-使用父指针-较慢
- 时间复杂度:O(n)-在最坏的情况下,我们可能会访问二叉树的所有节点。
- 空间复杂度:O(n)-空间利用了父指针哈希表,ancestor_set和queue,每个都为O(n)。
class Solution
{
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
{
HashMap<TreeNode, TreeNode> parent_map = new HashMap<>();
HashSet<TreeNode> ancestors_set = new HashSet<>();
Queue<TreeNode> queue = new LinkedList<>();
parent_map.put(root, null);
queue.add(root);
while(!parent_map.containsKey(p) || !parent_map.containsKey(q))
{
TreeNode node = queue.poll();
if(node.left != null)
{
parent_map.put(node.left, node);
queue.add(node.left);
}
if(node.right != null)
{
parent_map.put(node.right, node);
queue.add(node.right);
}
}
while(p != null)
{
ancestors_set.add(p);
p = parent_map.get(p);
}
while(!ancestors_set.contains(q))
q = parent_map.get(q);
return q;
}
}
答案 18 :(得分:0)
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null || root == p || root == q){
return root;
}
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
return left == null ? right : right == null ? left : root;
}
答案 19 :(得分:0)
这里的一些解决方案假设存在对根节点的引用,一些假设树是BST。
使用hashmap共享我的解决方案,而不参考root
节点和树可以是BST或非BST:
var leftParent : Node? = left
var rightParent : Node? = right
var map = [data : Node?]()
while leftParent != nil {
map[(leftParent?.data)!] = leftParent
leftParent = leftParent?.parent
}
while rightParent != nil {
if let common = map[(rightParent?.data)!] {
return common
}
rightParent = rightParent?.parent
}
答案 20 :(得分:0)
没有父节点,带有遍历的解决方案会给你O(n)时间复杂度,这是正确的。
遍历式方法 假设您正在为节点A和B找到LCA,最直接的方法是首先获取从根到A的路径,然后获取从根到B的路径。一旦有了这两个路径,就可以轻松地迭代它们并找到它们最后一个公共节点,它是A和B的最低共同祖先。
递归解决方案 另一种方法是使用递归。首先,我们可以从左树和右树(如果存在)获得LCA。如果A或B中的任何一个是根节点,则根是LCA,我们只返回根,这是递归的终点。当我们将树分成子树时,最终我们将击中A和B.
要组合子问题解决方案,如果LCA(左树)返回一个节点,我们知道A和B都位于左侧树中,返回的节点是最终结果。如果LCA(左)和LCA(右)都返回非空节点,则意味着A和B分别位于左侧和右侧树中。在这种情况下,根节点是最低的公共节点。
检查Lowest Common Ancestor以获取详细的分析和解决方案。
答案 21 :(得分:0)
广度优先搜索的代码以确保两个节点都在树中。 然后才进行LCA搜索。 如果您有任何改进建议,请发表评论。 我想我们可以标记它们被访问并在我们离开的某个点重新开始搜索以改进第二个节点(如果没有找到VISITED)
public class searchTree {
static boolean v1=false,v2=false;
public static boolean bfs(Treenode root, int value){
if(root==null){
return false;
}
Queue<Treenode> q1 = new LinkedList<Treenode>();
q1.add(root);
while(!q1.isEmpty())
{
Treenode temp = q1.peek();
if(temp!=null) {
q1.remove();
if (temp.value == value) return true;
if (temp.left != null) q1.add(temp.left);
if (temp.right != null) q1.add(temp.right);
}
}
return false;
}
public static Treenode lcaHelper(Treenode head, int x,int y){
if(head==null){
return null;
}
if(head.value == x || head.value ==y){
if (head.value == y){
v2 = true;
return head;
}
else {
v1 = true;
return head;
}
}
Treenode left = lcaHelper(head.left, x, y);
Treenode right = lcaHelper(head.right,x,y);
if(left!=null && right!=null){
return head;
}
return (left!=null) ? left:right;
}
public static int lca(Treenode head, int h1, int h2) {
v1 = bfs(head,h1);
v2 = bfs(head,h2);
if(v1 && v2){
Treenode lca = lcaHelper(head,h1,h2);
return lca.value;
}
return -1;
}
}
答案 22 :(得分:0)
这是C ++的做法。试图让算法尽可能简单易懂:
// Assuming that `BinaryNode_t` has `getData()`, `getLeft()` and `getRight()`
class LowestCommonAncestor
{
typedef char type;
// Data members which would behave as place holders
const BinaryNode_t* m_pLCA;
type m_Node1, m_Node2;
static const unsigned int TOTAL_NODES = 2;
// The core function which actually finds the LCA; It returns the number of nodes found
// At any point of time if the number of nodes found are 2, then it updates the `m_pLCA` and once updated, we have found it!
unsigned int Search (const BinaryNode_t* const pNode)
{
if(pNode == 0)
return 0;
unsigned int found = 0;
found += (pNode->getData() == m_Node1);
found += (pNode->getData() == m_Node2);
found += Search(pNode->getLeft()); // below condition can be after this as well
found += Search(pNode->getRight());
if(found == TOTAL_NODES && m_pLCA == 0)
m_pLCA = pNode; // found !
return found;
}
public:
// Interface method which will be called externally by the client
const BinaryNode_t* Search (const BinaryNode_t* const pHead,
const type node1,
const type node2)
{
// Initialize the data members of the class
m_Node1 = node1;
m_Node2 = node2;
m_pLCA = 0;
// Find the LCA, populate to `m_pLCANode` and return
(void) Search(pHead);
return m_pLCA;
}
};
如何使用它:
LowestCommonAncestor lca;
BinaryNode_t* pNode = lca.Search(pWhateverBinaryTreeNodeToBeginWith);
if(pNode != 0)
...
答案 23 :(得分:0)
试试这个
node * lca(node * root, int v1,int v2)
{
if(!root) {
return NULL;
}
if(root->data == v1 || root->data == v2) {
return root;}
else
{
if((v1 > root->data && v2 < root->data) || (v1 < root->data && v2 > root->data))
{
return root;
}
if(v1 < root->data && v2 < root->data)
{
root = lca(root->left, v1, v2);
}
if(v1 > root->data && v2 > root->data)
{
root = lca(root->right, v1, v2);
}
}
return root;
}
答案 24 :(得分:0)
还有一种方法。然而,它不如答案中已经建议的那样有效。
为节点n1创建路径向量。
为节点n2创建第二个路径向量。
暗示来自该节点的集合节点的路径向量将遍历以到达相关节点。
比较两个路径向量。它们不匹配的索引,返回该索引处的节点 - 1.这将给出LCA。
这种方法的缺点:
需要遍历树两次以计算路径向量。 需要额外的O(h)空间来存储路径向量。
然而,这也很容易实现和理解。
计算路径向量的代码:
private boolean findPathVector (TreeNode treeNode, int key, int pathVector[], int index) {
if (treeNode == null) {
return false;
}
pathVector [index++] = treeNode.getKey ();
if (treeNode.getKey () == key) {
return true;
}
if (findPathVector (treeNode.getLeftChild (), key, pathVector, index) ||
findPathVector (treeNode.getRightChild(), key, pathVector, index)) {
return true;
}
pathVector [--index] = 0;
return false;
}
答案 25 :(得分:0)
找到最低共同祖先的最简单方法是使用以下算法:
Examine root node if value1 and value2 are strictly less that the value at the root node Examine left subtree else if value1 and value2 are strictly greater that the value at the root node Examine right subtree else return root
public int LCA(TreeNode root, int value 1, int value 2) {
while (root != null) {
if (value1 < root.data && value2 < root.data)
return LCA(root.left, value1, value2);
else if (value2 > root.data && value2 2 root.data)
return LCA(root.right, value1, value2);
else
return root
}
return null;
}
答案 26 :(得分:0)
如果对伪代码(大学家庭作品)感兴趣的人就是其中一个。
GETLCA(BINARYTREE BT, NODE A, NODE B)
IF Root==NIL
return NIL
ENDIF
IF Root==A OR root==B
return Root
ENDIF
Left = GETLCA (Root.Left, A, B)
Right = GETLCA (Root.Right, A, B)
IF Left! = NIL AND Right! = NIL
return root
ELSEIF Left! = NIL
Return Left
ELSE
Return Right
ENDIF
答案 27 :(得分:0)
以下是c#(。net)中的两种方法(均在上面讨论过)供参考:
在二叉树中查找LCA的递归版本(O(N) - 最多访问每个节点) (解决方案的要点是LCA是(a)二叉树中的唯一节点,其中两个元素位于子树的任一侧(左侧和右侧)是LCA。(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;
}
BST(LCA) - 无关(仅供完成参考)
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);
}
}
答案 28 :(得分:0)
这就是我的想法,
复杂性: 步骤1:O(n),步骤2 = ~O(n),总= = O(n)。
答案 29 :(得分:0)
我找到了解决方案
根据3次遍历,您可以决定谁是LCA。 从LCA找到两个节点的距离。 添加这两个距离就是答案。
答案 30 :(得分:-1)
Php中的代码我假设树是一个数组二叉树。因此,您甚至不需要树来计算LCA。 输入:两个节点的索引 输出:LCA指数
<?php
global $Ps;
function parents($l,$Ps)
{
if($l % 2 ==0)
$p = ($l-2)/2;
else
$p = ($l-1)/2;
array_push($Ps,$p);
if($p !=0)
parents($p,$Ps);
return $Ps;
}
function lca($n,$m)
{
$LCA = 0;
$arr1 = array();
$arr2 = array();
unset($Ps);
$Ps = array_fill(0,1,0);
$arr1 = parents($n,$arr1);
unset($Ps);
$Ps = array_fill(0,1,0);
$arr2 = parents($m,$arr2);
if(count($arr1) > count($arr2))
$limit = count($arr2);
else
$limit = count($arr1);
for($i =0;$i<$limit;$i++)
{
if($arr1[$i] == $arr2[$i])
{
$LCA = $arr1[$i];
break;
}
}
return $LCA;//this is the index of the element in the tree
}
var_dump(lca(5,6));
?>
请告诉我是否有任何缺点。
答案 31 :(得分:-1)
//如果两个值都小于当前节点,则遍历左子树 //或者如果两个值都大于当前节点,则遍历右侧子树 //或者LCA是当前节点
public BSTNode findLowestCommonAncestor(BSTNode currentRoot, int a, int b){
BSTNode commonAncestor = null;
if (currentRoot == null) {
System.out.println("The Tree does not exist");
return null;
}
int currentNodeValue = currentRoot.getValue();
//If both the values are less than the current node then traverse the left subtree
//Or If both the values are greater than the current node then traverse the right subtree
//Or LCA is the current node
if (a < currentNodeValue && b < currentNodeValue) {
commonAncestor = findLowestCommonAncestor(currentRoot.getLeft(), a, b);
} else if (a > currentNodeValue && b > currentNodeValue) {
commonAncestor = findLowestCommonAncestor(currentRoot.getRight(), a, b);
} else {
commonAncestor = currentRoot;
}
return commonAncestor;
}
答案 32 :(得分:-2)
public class LeastCommonAncestor {
private TreeNode root;
private static class TreeNode {
TreeNode left;
TreeNode right;
int item;
TreeNode (TreeNode left, TreeNode right, int item) {
this.left = left;
this.right = right;
this.item = item;
}
}
public void createBinaryTree (Integer[] arr) {
if (arr == null) {
throw new NullPointerException("The input array is null.");
}
root = new TreeNode(null, null, arr[0]);
final Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
final int half = arr.length / 2;
for (int i = 0; i < half; i++) {
if (arr[i] != null) {
final TreeNode current = queue.poll();
final int left = 2 * i + 1;
final int right = 2 * i + 2;
if (arr[left] != null) {
current.left = new TreeNode(null, null, arr[left]);
queue.add(current.left);
}
if (right < arr.length && arr[right] != null) {
current.right = new TreeNode(null, null, arr[right]);
queue.add(current.right);
}
}
}
}
private static class LCAData {
TreeNode lca;
int count;
public LCAData(TreeNode parent, int count) {
this.lca = parent;
this.count = count;
}
}
public int leastCommonAncestor(int n1, int n2) {
if (root == null) {
throw new NoSuchElementException("The tree is empty.");
}
LCAData lcaData = new LCAData(null, 0);
// foundMatch (root, lcaData, n1, n2);
/**
* QQ: boolean was returned but never used by caller.
*/
foundMatchAndDuplicate (root, lcaData, n1, n2, new HashSet<Integer>());
if (lcaData.lca != null) {
return lcaData.lca.item;
} else {
/**
* QQ: Illegal thrown after processing function.
*/
throw new IllegalArgumentException("The tree does not contain either one or more of input data. ");
}
}
// /**
// * Duplicate n1, n1 Duplicate in Tree
// * x x => succeeds
// * x 1 => fails.
// * 1 x => succeeds by throwing exception
// * 1 1 => succeeds
// */
// private boolean foundMatch (TreeNode node, LCAData lcaData, int n1, int n2) {
// if (node == null) {
// return false;
// }
//
// if (lcaData.count == 2) {
// return false;
// }
//
// if ((node.item == n1 || node.item == n2) && lcaData.count == 1) {
// lcaData.count++;
// return true;
// }
//
// boolean foundInCurrent = false;
// if (node.item == n1 || node.item == n2) {
// lcaData.count++;
// foundInCurrent = true;
// }
//
// boolean foundInLeft = foundMatch(node.left, lcaData, n1, n2);
// boolean foundInRight = foundMatch(node.right, lcaData, n1, n2);
//
// if ((foundInLeft && foundInRight) || (foundInCurrent && foundInRight) || (foundInCurrent && foundInLeft)) {
// lcaData.lca = node;
// return true;
// }
// return foundInCurrent || (foundInLeft || foundInRight);
// }
private boolean foundMatchAndDuplicate (TreeNode node, LCAData lcaData, int n1, int n2, Set<Integer> set) {
if (node == null) {
return false;
}
// when both were found
if (lcaData.count == 2) {
return false;
}
// when only one of them is found
if ((node.item == n1 || node.item == n2) && lcaData.count == 1) {
if (!set.contains(node.item)) {
lcaData.count++;
return true;
}
}
boolean foundInCurrent = false;
// when nothing was found (count == 0), or a duplicate was found (count == 1)
if (node.item == n1 || node.item == n2) {
if (!set.contains(node.item)) {
set.add(node.item);
lcaData.count++;
}
foundInCurrent = true;
}
boolean foundInLeft = foundMatchAndDuplicate(node.left, lcaData, n1, n2, set);
boolean foundInRight = foundMatchAndDuplicate(node.right, lcaData, n1, n2, set);
if (((foundInLeft && foundInRight) ||
(foundInCurrent && foundInRight) ||
(foundInCurrent && foundInLeft)) &&
lcaData.lca == null) {
lcaData.lca = node;
return true;
}
return foundInCurrent || (foundInLeft || foundInRight);
}
public static void main(String args[]) {
/**
* Binary tree with unique values.
*/
Integer[] arr1 = {1, 2, 3, 4, null, 6, 7, 8, null, null, null, null, 9};
LeastCommonAncestor commonAncestor = new LeastCommonAncestor();
commonAncestor.createBinaryTree(arr1);
int ancestor = commonAncestor.leastCommonAncestor(2, 4);
System.out.println("Expected 2, actual " + ancestor);
ancestor = commonAncestor.leastCommonAncestor(2, 7);
System.out.println("Expected 1, actual " +ancestor);
ancestor = commonAncestor.leastCommonAncestor(2, 6);
System.out.println("Expected 1, actual " + ancestor);
ancestor = commonAncestor.leastCommonAncestor(2, 1);
System.out.println("Expected 1, actual " +ancestor);
ancestor = commonAncestor.leastCommonAncestor(3, 8);
System.out.println("Expected 1, actual " +ancestor);
ancestor = commonAncestor.leastCommonAncestor(7, 9);
System.out.println("Expected 3, actual " +ancestor);
// duplicate request
try {
ancestor = commonAncestor.leastCommonAncestor(7, 7);
} catch (Exception e) {
System.out.println("expected exception");
}
/**
* Binary tree with duplicate values.
*/
Integer[] arr2 = {1, 2, 8, 4, null, 6, 7, 8, null, null, null, null, 9};
commonAncestor = new LeastCommonAncestor();
commonAncestor.createBinaryTree(arr2);
// duplicate requested
ancestor = commonAncestor.leastCommonAncestor(8, 8);
System.out.println("Expected 1, actual " + ancestor);
ancestor = commonAncestor.leastCommonAncestor(4, 8);
System.out.println("Expected 4, actual " +ancestor);
ancestor = commonAncestor.leastCommonAncestor(7, 8);
System.out.println("Expected 8, actual " +ancestor);
ancestor = commonAncestor.leastCommonAncestor(2, 6);
System.out.println("Expected 1, actual " + ancestor);
ancestor = commonAncestor.leastCommonAncestor(8, 9);
System.out.println("Expected 8, actual " +ancestor); // failed.
}
}