《程序员面试金典》--寻找二叉树中两个节点的第一个公共祖先(三种情况)

转载地址:https://blog.csdn.net/zdplife/article/details/49424975

/****************************************************************************************************************

题目描述:
给定一颗二叉树,以及二叉树中的两个节点,找出这两个节点的第一个公共祖先节点。

*****************************************************************************************************************/

题目分析:

假设1:这个二叉树是二叉排序树(O(N))

      如果题目中的二叉树是二叉排序树,那么这道题目变的相对简单很多,我们只需要从根节点开始遍历,有以下三种情况发生:

      (1)如果题目给的两个节点的值都大于当前节点,那么继续遍历当前节点的右子树

      (2)如果题目给的两个节点的值都小于当前节点,那么继续遍历当前节点的左子树

      (3)如果当前节点大于其中一个节点而小于其中另外一个节点,那么则返回该节点,该节点便是两个节点的公共                  祖先节点。

假设2:这个二叉树是普通的二叉树,但是存在指向父节点的指针

       由于每个节点存在指向父节点的指针,所以如果给定任意一个节点,便可以找到从该节点到根节点的路径,因此我们可以找到题目中给予的两个节点分别到根节点的路径,因此该题目便变成了求两个单向链表的第一个公共节点。

假设2延伸:求两个单向链表第一个公共节点有两种方法:

      (1)第一种方法是:两个链表如果有公共节点,那么从第一个公共节点开始往后的所有节点都相同,我们首先遍历两个链表,求出两个链表的长度。然后设定两个指针分别指向这两个单向链表的头结点,首先让链表较长的指针先移动,直至移动的剩余长度与链表较短的那个链表长度相同为止,这个时候两个指针开始同时移动,直到两个指针指向的节点相同,这个节点便是这两个单向链表的第一个公共节点。

      (2)第二种方法是:可以设置两个栈,首先分别把两个链表中的节点依次入栈,接下来从两个栈中同时一个一个的将节点弹出,遇到第一对弹出的节点不同时,那么前一次弹出相同的节点便是这两个链表的第一个公共节点。

假设3:这个二叉树是普通的二叉树,并且不存在指向父节点的指针

<方法1>:判断子树中是否存在某节点(时间复杂度O(N^2))

          首先建立一个方法判断一棵树中是否存在某个节点,这个比较简单,只需要遍历整棵树,如果存在返回true,如果不存在则返回false。

      接下来的判断过程就相当于二叉排序数的判断过程了,只不过二叉排序树判断一个节点是否在左子树或者右子树中只需要和父节点比较,而普通的树需要遍历整个左子树或者右子树才能实现, 

      (1)如果题目给的两个节点都在右子树,那么继续遍历当前节点的右子树

      (2)如果题目给的两个节点都在左子树,那么继续遍历当前节点的左子树

      (3)如果给定的两个节点一个在左子树,一个在右子树,则返回当前节点。

<方法2>:寻找从根节点到子节点的路径

        如果找到从根节点到两个子节点的路径,那么题目就迎刃而解了,可以用栈遍历二叉树,最后找到从根节点到相应节点的路径,类似二叉树的先序非递归遍历一样,先遍历根节点,接着遍历左子树,接着遍历右子树,直到遍历到相应节点为止,这时栈中的元素便是路径元素。

<方法3>:递归遍历

      设根节点为root,两个节点分别为p,q,用前序遍历方法递归遍历整个二叉树,如果只找到p则返回p,如果只找到q则返回q,如果一个节点的左右子树分别找到p,q,则返回该节点,其他情况返回NULL。

程序代码如下:

  1. #include<iostream>
  2. using namespace std;
  3. struct BiTree
  4. {
  5. int val;
  6. BiTree* left,*right;
  7. };
  8. BiTree* commonAncestor(BiTree* root,BiTree*p,BiTree*q,bool &sign)
  9. {
  10. if(root== NULL)
  11. {
  12. sign= false;
  13. return NULL;
  14. }
  15. //p,q是同一个节点,则返回该节点,并标志找到了
  16. if(root==p&&root==q)
  17. {
  18. sign= true;
  19. return root;
  20. }
  21. BiTree* x=commonAncestor(root->left,p,q,sign);
  22. //如果在左子树中找到了公共节点,则直接返回
  23. if(sign== true)
  24. return x;
  25. BiTree* y=commonAncestor(root->right,p,q,sign);
  26. //如果在右子树中找到了公共节点,则直接返回
  27. if(sign== true)
  28. return y;
  29. //如果左子树和右子树分别找到了,则返回当前节点
  30. if(x!= NULL&&y!= NULL)
  31. {
  32. sign= true;
  33. return root;
  34. }
  35. //如果当前节点为p或者q,则返回当前节点
  36. if(root==p||root==q)
  37. return root;
  38. return x== NULL?y:x;
  39. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值