前言
最近刷到剑指offer 50题,树中两个结点的公共祖先,感觉这类题目对于不同性质的树会有不同的解法,而且也比较综合,在此就总结一下这类型的题目。
树为二叉搜索树
二叉搜索树的概念只要大家学过数据结构应该都是清楚的,也就是排序过的树。对于树上的每一个结点,其左子树的结点都小于父结点,其右子树的结点都大于父结点。那么我们解这道题的基本思想如下:
1)从根结点开始遍历,让其分别和输入的两个结点做比较。
2)如果当前结点比两个结点的值都大,那么最低公共祖先应该位于当前结点的左子树中。
3)如果当前结点比两个结点的值都小,那么最低公共祖先应该位于当前结点的右子树中。
4)如果当前结点在两个结点的值之间,那么最低公共祖先就是当前结点。
代码如下:
TreeNode* SameNode(TreeNode* pRoot,TreeNode *pNode1,TreeNode *pNode2){
if(pRoot==NULL||pNode1==NULL||pNode2==NULL)
return NULL;
int big=(pNode1->val>pNode2->val)?pNode1->val:pNode2->val;
int small=(pNode1->val<pNode2->val)?pNode1->val:pNode2->val;
TreeNode *res;
if(big==small){
cout<<"Two Node should different "<<endl;
return res;
}
if((small<pRoot->val)&&(big>pRoot->val)){
return pRoot;
}else if((small<pRoot->val)&&(big<pRoot->val)){
res=SameNode(pRoot->left,pNode1,pNode2);
}else if((small>pRoot->val)&&(big>pRoot->val)){
res=SameNode(pRoot->right,pNode1,pNode2);
}
return res;
}
树为普通二叉树,有父指针
如果树中的每个结点,除根结点外,都有一个指向父结点的指针,那么这个问题就可以转换成求两个链表的第一个公共结点。例如假设树结点中指向父结点的指针是parent, 那么从树的每一个叶子结点开始都有一个由指针parent串起来的链表,这些链表的尾指针都是树的根结点。输入两个结点,那么这两个结点位于两个链表上,他们的最低公共祖先也就是两个链表的第一个公共结点。如输入D跟F,D所在的链表为D-B-A, F所在链表为F-C-A,所以最低公共祖先为A。代码如下:
int GetLength(TreeNode *pNode) {
int len = 0;
TreeNode *p = pNode;
while (p != NULL) {
len++;
p = p->parent;
}
return len;
}
TreeNode *walkstep(TreeNode *pNode, int step) {
while (step--) {
pNode = pNode->parent;
}
return pNode;
}
TreeNode* SameNode(TreeNode* pRoot, TreeNode *pNode1, TreeNode *pNode2) {
if (pRoot == NULL || pNode1 == NULL || pNode2 == NULL)
return NULL;
int n1 = GetLength(pNode1);
int n2 = GetLength(pNode2);
if (n1>n2) {
pNode1 = walkstep(pNode1, n1 - n2);
}
else {
pNode2 = walkstep(pNode2, n2 - n1);
}
if (pNode1 == pNode2) {
return pNode1->parent;
}
while (pNode1 != NULL) {
if (pNode1 == pNode2) {
return pNode1;
}
else {
pNode1 = pNode1->parent;
pNode2 = pNode2->parent;
}
}
return NULL;
}
树为普通二叉树,无父指针
在这样的情况下,我们可以首先利用前序遍历来得到根结点到某个结点的路径。当遍历两个得到输入结点的两条路径,问题转换成了求两个路径构成的链表的最后公共结点。代码如下:
#include <iostream>
#include <vector>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int val) :val(val), left(NULL), right(NULL) {}
};
bool GetNodePath(TreeNode* pHead, TreeNode* pNode, vector<TreeNode*>& path)
{
if (pHead == pNode)
return true;
path.push_back(pHead);
bool found = false;
if (pHead->left != NULL)
found = GetNodePath(pHead->left, pNode, path);
if (!found && pHead->right)
found = GetNodePath(pHead->right, pNode, path);
if (!found)
path.pop_back();
return found;
}
TreeNode *GetLastCommonNode(const vector<TreeNode*> &path1, const vector<TreeNode*> &path2) {
vector<TreeNode*>::const_iterator it1 = path1.begin();
vector<TreeNode*>::const_iterator it2 = path2.begin();
TreeNode *plast = NULL;
while (it1 != path1.end() && it2 != path2.end()) {
if (*it1 == *it2) {
plast = *it1;
}
it1++;
it2++;
}
return plast;
}
TreeNode* SameNode(TreeNode* pRoot, TreeNode *pNode1, TreeNode *pNode2) {
if (pRoot == NULL || pNode1 == NULL || pNode2 == NULL)
return NULL;
vector<TreeNode*> path1;
GetNodePath(pRoot, pNode1, path1);
vector<TreeNode*> path2;
GetNodePath(pRoot, pNode2, path2);
return GetLastCommonNode(path1, path2);
}
int main() {
/*
//
// 4
// / \
// 2 6
// / \ / \
// 1 3 5 7
*/
TreeNode *n1 = new TreeNode(1);
TreeNode *n2 = new TreeNode(2);
TreeNode *n3 = new TreeNode(3);
TreeNode *n4 = new TreeNode(4);
TreeNode *n5 = new TreeNode(5);
TreeNode *n6 = new TreeNode(6);
TreeNode *n7 = new TreeNode(7);
n4->left = n2;
n4->right = n6;
n2->left = n1;
n2->right = n3;
n6->left = n5;
n6->right = n7;
TreeNode *res = SameNode(n4, n1, n3);
cout << res->val << endl;
// 8
// /
// 9
// /
// 10
// /
// 11
// /
// 12
TreeNode *n8 = new TreeNode(8);
TreeNode *n9 = new TreeNode(9);
TreeNode *n10 = new TreeNode(10);
TreeNode *n11 = new TreeNode(11);
TreeNode *n12 = new TreeNode(12);
n8->left = n9;
n9->left = n10;
n10->left = n11;
n11->left = n12;
TreeNode *res1 = SameNode(n8, n9, n10);
cout << res1->val << endl;
}
总结
在求树的相关问题中,我们在找不到思路的时候,可以尝试着让树转换成链表的相关问题,再尝试着去解决,说不定这里会有喷火龙!