树中两个节点最低公共祖先系列算法

来源:互联网 发布:ansys软件 编辑:程序博客网 时间:2024/06/05 15:21

前言

最近刷到剑指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;}

总结

在求树的相关问题中,我们在找不到思路的时候,可以尝试着让树转换成链表的相关问题,再尝试着去解决,说不定这里会有喷火龙!

原创粉丝点击