案例一:
面试题49把字符串转换成整数
案例二:
面试题50树中两个节点的最低公共祖先
题目:
输入两个树节点,求它们的最低公共祖先。
1.如果是二叉搜索树,二叉搜索树是排序过的,位于左子树的节点都比父节点小,位于右子树的节点都比父节点大。
我们只需要从树的根节点开始和两个输入的节点进行比较。如果当前节点的值比两个节点的值都大,最低公共祖先一定在当前节点的左子树中,下一步遍历当前节点的左子节点。如果当前节点的值比两个节点的值都小,那么最低公共父节点一定在当前节点的右子树中,于是下一步遍历当前节点的右子节点。
2.如果不是二叉搜索树,但是树的节点中有指向父节点的指针。这个问题可以转换为求两个链表的第一个公共节点。
从树的每一个叶子节点开始都有一个由指向父节点的指针串起来的链表,这些链表的尾指针都是树的根节点。
输入两个节点,那么这两个节点位于两个链表上,他们的最低公共祖先刚好就是这两个链表的第一个公共节点。
比如输入F和H,F在链表F-D-B-A上,而H在链表H-E-B-A上,这两个链表的第一个交点B刚好也是它们的最低公共祖先。
3.不是二叉搜索树,树的节点中也没有指向父节点的指针。
用两个链表分别保存从根节点到输入的两个节点的路径,然后把问题转换成两个链表的最后公共节点。
需要遍历两次树得到两个链表,每次时间复杂度O(n),
得到的两条路径的长度在最差情况下是O(n),通常情况下两条路径的长度是O(logn)。
整个程序的时间复杂度3O(n),空间复杂度是2O(n)。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool getNodePath(TreeNode* pRoot, TreeNode* pNode, list<TreeNode*> &path){
if(pRoot==pNode){
path.push_back(pNode);//与剑指offer不同,用来解决p可能是q的祖先
return true;
}
path.push_back(pRoot);
bool found = false;
if(pRoot->left)found = getNodePath(pRoot->left, pNode, path);
if(!found&&pRoot->right){
found = getNodePath(pRoot->right, pNode, path);
}
if(!found)path.pop_back();
return found;
}
TreeNode* getLastCommonNode(const list<TreeNode*> &path1, const list<TreeNode*> &path2){
list<TreeNode*>::const_iterator p1 = path1.begin();
list<TreeNode*>::const_iterator p2 = path2.begin();
TreeNode* result = nullptr;
while(p1!=path1.end()&&p2!=path2.end()){
if(*p1==*p2) result = *p1;
++p1;
++p2;
}
return result;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==nullptr||p==nullptr||q==nullptr)return nullptr;
list<TreeNode*> path1, path2;
getNodePath(root, p, path1);
getNodePath(root, q, path2);
return getLastCommonNode(path1, path2);
}
};