来源:http://blog.csdn.net/wangyangkobe/article/details/6527858
分析:如果我们要找3和8这两个节点的公共父亲节点,我们的做法是首先找到3到根节点的路劲,然后找到8到根节点的路径。
可以看到, 这里的问题实际上是另一个我们熟知的问题,有2个相交的单链表,找出它们的相交点!
- //节点结构体(包含父节点)
- struct Node
- {
- Node *lChild;
- Node *rChild;
- Node *parent;
- int element;
- Node(int ele, Node *m_parent = NULL, Node *left = NULL, Node *right = NULL)
- :element(ele), parent(m_parent), lChild(left), rChild(right){}
- };
- //创建树
- Node* CreateTree(Node *root, int arr[], int cur, int len)
- {
- root = new Node(arr[cur], root); //根节点的父节点为null
- if (2*cur + 1 < len)
- root->lChild = CreateTree(root, arr, 2*cur + 1, len);
- if (2*cur + 2 < len)
- root->rChild = CreateTree(root, arr, 2*cur + 2, len);
- return root;
- }
- //返回节点值为element的节点指针
- Node *FindNode(Node *root, int element)
- {
- if (NULL == root)
- return NULL;
- else if (element == root->element)
- return root;
- else
- {
- Node *result = NULL;
- Node *l_res = FindNode(root->lChild, element);
- Node *r_res = FindNode(root->rChild, element);
- if(NULL != l_res)
- result = l_res;
- else if(NULL != r_res)
- result = r_res;
- return result;
- }
- }
- //返回节点node到根节点的root的距离
- int GetLengthToRoot(Node *root, Node *node)
- {
- assert(node && root);
- int res = 0;
- while (node->parent != root)
- {
- res++;
- node = node->parent;
- }
- return res;
- }
- //函数功能:查找节点node1和node2的最近公共父节点
- //算法思路:和求两个链表的节点的方法一样
- //A先走到root,计数为An,B走到Root,计数为Bn,不失一般性,假设An>Bn,
- //那么再来一次,A先往parent方向走An-Bn步,然后两个一起走,碰头(遇到相同节点)的时候就是最近公共父节点了
- Node *FindNearestCommonParent(Node *root, Node *node1, Node *node2)
- {
- int path1 = GetLengthToRoot(root, node1);
- int path2 = GetLengthToRoot(root, node2);
- Node *longPath = (path1 > path2) ? node1 : node2;
- Node *shortPath = (path1 > path2) ? node2 : node1;
- int step = abs(path2 - path1);
- for(int i = 0; i < step; i++)
- longPath = longPath->parent;
- while (longPath && shortPath)
- {
- if(longPath == shortPath)
- return longPath;
- longPath = longPath->parent;
- shortPath = shortPath->parent;
- }
- }
- int main()
- {
- int arr[] = {10, 6, 14, 4, 8, 12, 16, 3, 5};
- int len = sizeof(arr)/ sizeof(*arr);
- Node *root = NULL;
- root = CreateTree(root, arr, 0, len);
- Node *node1 = FindNode(root, 3);
- Node *node2 = FindNode(root, 8);
- Node *common = FindNearestCommonParent(root, node1, node2);
- cout<<"The nearest common parent node is: "<<common->element<<endl;
- system("pause");
- }
情况2. 节点只有left/right,没有parent指针,root已知.
- //节点结构体
- struct Node
- {
- Node *lChild;
- Node *rChild;
- int element;
- Node(int ele, Node *left = NULL, Node *right = NULL)
- :element(ele), lChild(left), rChild(right){}
- };
- //创建树
- Node* CreateTree(Node *root, int arr[], int cur, int len)
- {
- root = new Node(arr[cur]);
- if (2*cur + 1 < len)
- root->lChild = CreateTree(root, arr, 2*cur + 1, len);
- if (2*cur + 2 < len)
- root->rChild = CreateTree(root, arr, 2*cur + 2, len);
- return root;
- }
- //返回节点值为element的节点指针
- Node *FindNode(Node *root, int element)
- {
- if (NULL == root)
- return NULL;
- else if (element == root->element)
- return root;
- else
- {
- Node *result = NULL;
- Node *l_res = FindNode(root->lChild, element);
- Node *r_res = FindNode(root->rChild, element);
- if(NULL != l_res)
- result = l_res;
- else if(NULL != r_res)
- result = r_res;
- return result;
- }
- }
- //函数功能:查找节点node1和node2的最近公共父节点,result为最近公共祖先节点的指针的指针
- //算法思想:如果一个节点的左子树包含p,q中的一个节点,右子树包含另一个,则这个节点就是p,q的最近公共祖先。
- int FindNearestCommonParent(Node *root, Node *node1, Node *node2, Node **result)
- {
- if(NULL == root)
- return 0;
- if(root == node1 || root == node2)
- return 1;
- int left = FindNearestCommonParent(root->lChild, node1, node2, result);
- if (2 == left)
- return 2;
- int right = FindNearestCommonParent(root->rChild, node1, node2, result);
- if(2 == right)
- return 2;
- if (left + right == 2)
- {
- *result = root;
- return 2;
- }
- }
- int main()
- {
- int arr[] = {10, 6, 14, 4, 8, 12, 16, 3, 5};
- int len = sizeof(arr)/ sizeof(*arr);
- Node *root = NULL;
- root = CreateTree(root, arr, 0, len);
- Node *node1 = FindNode(root, 3);
- Node *node2 = FindNode(root, 16);
- Node *result = NULL;
- int res = FindNearestCommonParent(root, node1, node2, &result);
- if(2 == res)
- cout<<result->element<<endl;
- system("pause");
- }
情况3. 二叉树是个二叉查找树,且root和两个节点的值(a, b)已知。
- //节点结构体
- struct Node
- {
- Node *lChild;
- Node *rChild;
- int element;
- Node(int ele, Node *left = NULL, Node *right = NULL)
- :element(ele), lChild(left), rChild(right){}
- };
- //函数功能:递归想二叉查找树中插入节点
- void InsertNode(Node *&root, int ele)
- {
- if (root == NULL)
- root = new Node(ele);
- else if(ele < root->element)
- InsertNode(root->lChild, ele);
- else if (ele > root->element)
- InsertNode(root->rChild, ele);
- }
- //创建树
- Node* CreateBSTree(int arr[], int len)
- {
- Node *root = NULL;
- for (int i = 0; i < len; i++)
- {
- InsertNode(root, arr[i]);
- }
- return root;
- }
- //返回节点值为element的节点指针
- Node *FindNode(Node *root, int element)
- {
- if (NULL == root)
- return NULL;
- else if (element == root->element)
- return root;
- else
- {
- Node *result = NULL;
- Node *l_res = FindNode(root->lChild, element);
- Node *r_res = FindNode(root->rChild, element);
- if(NULL != l_res)
- result = l_res;
- else if(NULL != r_res)
- result = r_res;
- return result;
- }
- }
- //函数功能:查找节点node1和node2的最近公共父节点,result为最近公共祖先节点的指针的指针
- //算法思想:充分利用二叉查找数的性质
- Node* FindNearestCommonParent(Node *root, Node *node1, Node *node2)
- {
- while (root != NULL)
- {
- int ele = root->element;
- if (ele < node1->element && ele < node2->element)
- root = root->rChild;
- else if (ele > node1->element && ele > node2->element)
- root = root->lChild;
- else
- break;
- }
- return root;
- }
- int main()
- {
- int arr[] = {10, 6, 14, 4, 8, 12, 16, 3, 5};
- int len = sizeof(arr)/ sizeof(*arr);
- Node *root = NULL;
- root = CreateBSTree(arr, len);
- Node *node1 = FindNode(root, 3);
- Node *node2 = FindNode(root, 5);
- Node *res = FindNearestCommonParent(root, node1, node2);
- cout<<res->element<<endl;
- system("pause");
- }
**************************************************************************************************
实际上,用树的后序遍历就可以了。当访问到所求的节点A时,如果这两个节点不在一条线上,则它们必定分别在A的左子树和右子树上,后序遍历到第一个满足这个条件的节点就是所要求的节点A。另外,还必须对这两个节点在一条线上的情况,做特殊处理。
代码:
static bool lca(Node *root, int va, int vb, Node *&result, Node* parrent)
{
// left/right 左/右子树是否含有要判断的两节点之一
bool left = false, right = false;
if (!result && root->left) left = lca(root->left,va,vb,result,root);
if (!result && root->right) right = lca(root->right,va,vb,result,root);
// mid 当前节点是否是要判断的两节点之一
bool mid = false;
if (root->data == va || root->data == vb) mid=true;
if (!result && int(left + right + mid) == 2) {
if (mid) result = parrent;
else result = root;
}
return left | mid | right ;
}
Node *lca(Node *root,int va, int vb)
{
if (root == NULL) return NULL;
Node *result = NULL;
lca(root, va, vb,result, NULL);
return result;
}
{
// left/right 左/右子树是否含有要判断的两节点之一
bool left = false, right = false;
if (!result && root->left) left = lca(root->left,va,vb,result,root);
if (!result && root->right) right = lca(root->right,va,vb,result,root);
// mid 当前节点是否是要判断的两节点之一
bool mid = false;
if (root->data == va || root->data == vb) mid=true;
if (!result && int(left + right + mid) == 2) {
if (mid) result = parrent;
else result = root;
}
return left | mid | right ;
}
Node *lca(Node *root,int va, int vb)
{
if (root == NULL) return NULL;
Node *result = NULL;
lca(root, va, vb,result, NULL);
return result;
}