二叉树最近公共父节点

在二叉树中找最近公共父节点。分为两种情况,一种是有父指针,一种没有父指针。

1、有父指针

这种情况比较简单,计算两个结点的深度,再把深度大的向上移,移到同一深度。在同时向上移动,直到两个结点相同,这样便找到了父节点。这个算法时间复杂度为O(N)。
代码实现:
[cpp]  view plain  copy
  1. #include<iostream>  
  2. struct Node  
  3. {  
  4.     int data;  
  5.     Node* left;  
  6.     Node* right;  
  7.     Node* parent;  
  8.     Node() :left(NULL), right(NULL), parent(NULL)  
  9.     {}  
  10. };  
  11. int getDpeth(Node *n)//结点n到根节点深度  
  12. {  
  13.     int count = 0;  
  14.     while (n)  
  15.     {  
  16.         ++count;  
  17.         n = n->parent;  
  18.     }  
  19.     return count;  
  20. }  
  21. Node* findNearestCommonAncestor(Node* n1, Node* n2)  
  22. {  
  23.     int depth1 = getDpeth(n1);  
  24.     int depth2 = getDpeth(n2);  
  25.   
  26.     //移动同一深度  
  27.     while (depth1 > depth2)  
  28.     {  
  29.         n1 = n1->parent;  
  30.         --depth1;  
  31.     }  
  32.     while (depth1 < depth2)  
  33.     {  
  34.         n2 = n2->parent;  
  35.         --depth2;  
  36.     }  
  37.     //向上找  
  38.     while (n1 != n2)  
  39.     {  
  40.         n1 = n1->parent;  
  41.         n2 = n2->parent;  
  42.     }  
  43.     return n1;  
  44. }  
  45.   
  46. int main()  
  47. {  
  48.     //测试  
  49.     Node* A[11];  
  50.     for (int i = 0; i < 11; ++i)  
  51.     {  
  52.         A[i] = new Node();  
  53.         A[i]->data = i;  
  54.     }  
  55.   
  56.     for (int i = 0; i < 5; ++i)  
  57.     {  
  58.         A[i]->left = A[i * 2 + 1];  
  59.         A[i * 2 + 1]->parent = A[i];  
  60.   
  61.         A[i]->right = A[i * 2 + 2];  
  62.         A[i * 2 + 2]->parent = A[i];  
  63.     }  
  64.   
  65.     Node* Ancestor = findNearestCommonAncestor(A[7], A[6]);  
  66.   
  67.   
  68. }  

2、没有父指针

这种情况有点难。首先从根节点开始向下找,如果根节点等于其中一个子节点,那么根节点便是最近公共父结点。否则计算左子树和右子树中包含n1或n2的个数。如果左子树包含n1、n2那么最近公共父结点在左子树,如果右子树包含n1和n2,那么在右子树。如果左右子树各包含一个,那么最近公共父结点就是当前结点。如果二叉树是平衡的,那么算法复杂度为O(logN)。最坏情况就是树成了链表,算法时间负责度为O(N^2)。
思路清晰了,可以编写代码:
[cpp]  view plain  copy
  1. #include<iostream>  
  2. struct Node  
  3. {  
  4.     int data;  
  5.     Node* left;  
  6.     Node* right;  
  7.     Node() :left(NULL), right(NULL)  
  8.     {}  
  9. };  
  10. //计算当前结点包含n1、n2个数  
  11. int countMatch(Node *current, Node* n1, Node* n2)  
  12. {  
  13.     if (current == NULL)  
  14.         return 0;  
  15.     int count = countMatch(current->left, n1, n2) + countMatch(current->right, n1, n2);  
  16.     if (current == n1 || current == n2)  
  17.         return 1 + count;  
  18.     return count;     
  19. }  
  20. Node* findLCA(Node* root, Node* n1, Node* n2)  
  21. {  
  22.     if (root == NULL)  
  23.         return NULL;  
  24.     if (root == n1 || root == n2)  
  25.         return root;  
  26.     int count = countMatch(root->left, n1, n2);//左子树包含n1和n2的个数  
  27.     if (count == 1)  
  28.         return root;//左子树一个,右子树肯定也有一个  
  29.     else if (count == 2)//都在左子树  
  30.         return findLCA(root->left, n1, n2);  
  31.     else//都在右子树  
  32.         return findLCA(root->right, n1, n2);  
  33. }  
  34. int main()  
  35. {  
  36.     //测试  
  37.     Node* A[11];  
  38.     for (int i = 0; i < 11; ++i)  
  39.     {  
  40.         A[i] = new Node();  
  41.         A[i]->data = i;  
  42.     }  
  43.   
  44.     for (int i = 0; i < 5; ++i)  
  45.     {  
  46.         A[i]->left = A[i * 2 + 1];  
  47.           
  48.         A[i]->right = A[i * 2 + 2];  
  49.       
  50.     }  
  51.   
  52.     Node* Ancestor = findLCA(A[0],A[7], A[10]);  
  53.   
  54.   
  55. }  


还有一种方法,从下向上找。如果找到n1或n2,就把它传给它的父结点,如果向下到头都没有找到,那么返回NULL。如果当前结点左右子树都返回非NULL,那么当前结点就是最近公共父结点。这样只需要遍历一遍,算法时间复杂度为O(N)。
[cpp]  view plain  copy
  1. #include<iostream>  
  2. struct Node  
  3. {  
  4.     int data;  
  5.     Node* left;  
  6.     Node* right;  
  7.     Node() :left(NULL), right(NULL)  
  8.     {}  
  9. };  
  10. Node* findLCA(Node *root, Node* n1, Node* n2)  
  11. {  
  12.     if (root == NULL)//没找到  
  13.         return NULL;  
  14.     if (root == n1 || root == n2)//找到  
  15.         return root;  
  16.     Node* L = findLCA(root->left, n1, n2);//左子树  
  17.     Node* R = findLCA(root->right, n1, n2);//右子树  
  18.     //当前结点左右子树都找到了n1和n2,那么这个结点就是LCA结点  
  19.     if (L != NULL&R != NULL)  
  20.         return root;  
  21.     //否则是不为NULL的结点,或者两个都为NULL  
  22.     else  
  23.         return L !=NULL ? L : R;  
  24. }  
  25.   
  26. int main()  
  27. {  
  28.     //测试  
  29.     Node* A[11];  
  30.     for (int i = 0; i < 11; ++i)  
  31.     {  
  32.         A[i] = new Node();  
  33.         A[i]->data = i;  
  34.     }  
  35.   
  36.     for (int i = 0; i < 5; ++i)  
  37.     {  
  38.         A[i]->left = A[i * 2 + 1];  
  39.   
  40.         A[i]->right = A[i * 2 + 2];  
  41.   
  42.     }  
  43.   
  44.     Node* Ancestor = findLCA(A[0], A[7], A[10]);  
  45.   
  46.   
  47. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值