剑指Offer(第二版)面试题68:树中两个节点的最低公共祖先
题目要求:
输入一棵树的根节点,输入两个被观察节点,求这两个节点的最低(最近)公共祖先。
解题思路:
此题比较开放,主要是对于“树”没有做明确说明,所以原书中就对树的可能情况做了假设,然后就衍生出多种思路。这道题表面上是一道题,实际上是一组题。所以,在动笔之前一定要和面试官沟通清楚。
- 是否是二叉排序树。
- 若不是,是否有指向父节点的指针。
- 若没有,是否允许额外的存储空间。
1)如果是二叉,搜索树:
遍历找到比第一个节点大,比第二个节点小的节点即可
2)如果是父子间有双向指针的树:
由下往上看,转化为找两个链表的第一个公共节点问题
3)如果只是一个包含父到子的指针的普通树:
3.1)如果不能使用额外空间,从根节点开始判断他的子树是否包含那两个节点,找到最小的的子树即可
时间复杂度o(n^2)(此为最差,平均不太好算。。。),空间复杂度为o(1)
3.2) 如果能用额外空间,可以遍历两次(深度优先)获取根节点到那两个节点的路径,然后求两个路径的最后一个公共节点
时间复杂度o(n),空间复杂度o(logn)
作者:ryderchan
链接:https://www.jianshu.com/p/edffe43abc14
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(1)(2)比较简单。下面仅对(3),以下图所示的树为例,进行思路实现与求解验证
// 形状普通的树
// 1
// / \
// 2 3
// / \
// 4 5
// / \ / | \
// 6 7 8 9 10
参考代码
#include <iostream>
#include <vector>
#include <list>
#include <string>
using namespace std;
struct TreeNode
{
int m_nValue;
vector<TreeNode*> m_vChildren;
};
void Test1();
void ConnectTreeNodes(TreeNode* pParent, TreeNode* pChild);
void Test( string testName, TreeNode* pRoot, TreeNode* pNode1, TreeNode* pNode2, TreeNode* pExpected);
TreeNode* CreateTreeNode(int value);
bool GetNodePath(TreeNode* pRoot, TreeNode* pNode, list<TreeNode*>& path);
TreeNode* GetLastCommonNode(const list<TreeNode*>& path1,const list<TreeNode*>& path2);
TreeNode* GetLastCommonParent(TreeNode* pRoot, TreeNode* pNode1,TreeNode* pNode2);
int main()
{
cout<<"Test(Test1, pNode1, pNode6, pNode8, pNode2);"<<endl;
Test1();
}
bool GetNodePath(TreeNode* pRoot, TreeNode* pNode, list<TreeNode*>& path)
{
if(pRoot == pNode)
return true;
path.push_back(pRoot);
bool found = false;
vector<TreeNode*>::iterator i = pRoot->m_vChildren.begin();
while(!found && i < pRoot->m_vChildren.end())
{
found = GetNodePath(*i,pNode,path);
++i;
}
if(!found)
path.pop_back();
return found;
}
TreeNode* GetLastCommonNode(const list<TreeNode*>& path1, const list<TreeNode*>& path2)
{
list<TreeNode*>::const_iterator iterator1 = path1.begin();
list<TreeNode*>::const_iterator iterator2 = path2.begin();
TreeNode* pLast = nullptr;
while(iterator1 != path1.end() && iterator2 != path2.end())
{
if(*iterator1 == *iterator2)
pLast = *iterator1;
++iterator1;
++iterator2;
}
return pLast;
}
TreeNode* GetLastCommonParent(TreeNode* pRoot, TreeNode* pNode1,TreeNode* pNode2)
{
if(pRoot == nullptr || pNode1 == nullptr || pNode2 == nullptr)
return nullptr;
list<TreeNode*> path1;
GetNodePath(pRoot,pNode1,path1);
list<TreeNode*> path2;
GetNodePath(pRoot,pNode2,path2);
return GetLastCommonNode(path1,path2);
}
TreeNode* CreateTreeNode(int value)
{
TreeNode* pNode = new TreeNode();
pNode->m_nValue = value;
return pNode;
}
void ConnectTreeNodes(TreeNode* pParent, TreeNode* pChild)
{
if(pParent != nullptr)
{
pParent->m_vChildren.push_back(pChild);
}
}
// ====================测试代码====================
void Test( string testName, TreeNode* pRoot, TreeNode* pNode1, TreeNode* pNode2, TreeNode* pExpected)
{
if(!testName.empty())
cout << testName << " begins : ";
TreeNode* pResult = GetLastCommonParent(pRoot, pNode1, pNode2);
if((pExpected == nullptr && pResult == nullptr) ||
(pExpected != nullptr && pResult != nullptr && pResult->m_nValue == pExpected->m_nValue))
printf("Passed.\n");
else
printf("Failed.\n");
}
// 形状普通的树
// 1
// / \
// 2 3
// / \
// 4 5
// / \ / | \
// 6 7 8 9 10
void Test1()
{
TreeNode* pNode1 = CreateTreeNode(1);
TreeNode* pNode2 = CreateTreeNode(2);
TreeNode* pNode3 = CreateTreeNode(3);
TreeNode* pNode4 = CreateTreeNode(4);
TreeNode* pNode5 = CreateTreeNode(5);
TreeNode* pNode6 = CreateTreeNode(6);
TreeNode* pNode7 = CreateTreeNode(7);
TreeNode* pNode8 = CreateTreeNode(8);
TreeNode* pNode9 = CreateTreeNode(9);
TreeNode* pNode10 = CreateTreeNode(10);
ConnectTreeNodes(pNode1, pNode2);
ConnectTreeNodes(pNode1, pNode3);
ConnectTreeNodes(pNode2, pNode4);
ConnectTreeNodes(pNode2, pNode5);
ConnectTreeNodes(pNode4, pNode6);
ConnectTreeNodes(pNode4, pNode7);
ConnectTreeNodes(pNode5, pNode8);
ConnectTreeNodes(pNode5, pNode9);
ConnectTreeNodes(pNode5, pNode10);
Test("Test1", pNode1, pNode6, pNode8, pNode2);
}
运行结果
Test(Test1, pNode1, pNode6, pNode8, pNode2);
Test1 begins : Passed.