From: http://zhedahht.blog.163.com/blog/static/25411174201081263815813/
Question: enter two nodes in the binary tree and output the lowest common parent node among the two nodes. The binary tree node is defined as follows:
struct TreeNode
{
int m_nvalue;
TreeNode* m_pLeft;
TreeNode* m_pRight;
};
Analysis: finding the lowest common node of the two nodes in the number is a common problem during the interview. There are at least two variants of this problem:
The first variant is that the binary tree is a special Binary Tree: searches for a binary tree, that is, the tree is sorted, and the node on the left Tree is smaller than the parent node, the node in the right subtree is larger than the parent node. We only need to compare the two nodes from the root node: if the value of the current node is larger than that of the two nodes, the lowest common parent node must be in the left subtree of the current node; if the value of the current node is smaller than that of the two nodes, the lowest common parent node must be in the right subtree of the current node.
The second variant is that the tree is not necessarily a binary tree, and each node has a pointer pointing to its parent node. So we can get a one-way linked list that reaches the root node from any node, so this problem is converted to the first public node of two one-way linked lists.
Now let's go back to this issue. The so-called common parent node is that both nodes appear in the Child tree of this node. Therefore, we can define a function to determine whether a node's subtree contains another node. This is not a very difficult task. We can implement it using recursive methods:
// If the tree with head pHead has a node pNode, return true. Otherwise return false.
bool HasNode(TreeNode* pHead, TreeNode* pNode)
{
if(pHead == pNode) return true;
bool has = false;
if(pHead->m_pLeft != NULL)
has = HasNode(pHead->m_pLeft, pNode);
if(!has && pHead->m_pRight != NULL)
has = HasNode(pHead->m_pRight, pNode);
return has;
}
We can start from the root node and determine whether the left and right subtree of the tree with the current node as the root contains the two nodes we are looking. If both nodes appear in its left subtree, the lowest common parent node also appears in its left subtree. If both nodes appear in its right subtree, the lowest common parent node also appears in its right subtree. If one of the two nodes appears in the left subtree and the other appears in the right subtree
Is the lowest common parent node. Based on this idea, we can write the following code:
// Find the last parent of pNode1 and pNode2 in a tree with head pHead
TreeNode* LastCommonParent_1(TreeNode* pHead, TreeNode* pNode1, TreeNode* pNode2)
{
if(pHead == NULL || pNode1 == NULL || pNode2 == NULL)
return NULL;
// check whether left child has pNode1 and pNode2
bool leftHasNode1 = false;
bool leftHasNode2 = false;
if(pHead->m_pLeft != NULL) {
leftHasNode1 = HasNode(pHead->m_pLeft, pNode1);
leftHasNode2 = HasNode(pHead->m_pLeft, pNode2);
}
if(leftHasNode1 && leftHasNode2) {
if(pHead->m_pLeft == pNode1 || pHead->m_pLeft == pNode2)
return pHead;
return LastCommonParent_1(pHead->m_pLeft, pNode1, pNode2);
}
// check whether right child has pNode1 and pNode2
bool rightHasNode1 = false;
bool rightHasNode2 = false;
if(pHead->m_pRight != NULL) {
if(!leftHasNode1)
rightHasNode1 = HasNode(pHead->m_pRight, pNode1);
if(!leftHasNode2)
rightHasNode2 = HasNode(pHead->m_pRight, pNode2);
}
if(rightHasNode1 && rightHasNode2) {
if(pHead->m_pRight == pNode1 || pHead->m_pRight == pNode2)
return pHead;
return LastCommonParent_1(pHead->m_pRight, pNode1, pNode2);
}
if((leftHasNode1 && rightHasNode2) || (leftHasNode2 && rightHasNode1))
return pHead;
return NULL;
}
Next, let's analyze the efficiency of this method. The essence of the hasnode function is to traverse a tree. Its time complexity is O (n) (N is the number of nodes in the tree ). Since we start with the root node, we need to call the hasnode function for each node. Therefore, the total time complexity is O (n2 ).
By carefully analyzing the above code, we can easily find that we need to traverse every node of the tree to determine whether a node-based tree contains a node. Next, we can determine whether the tree with the Left or Right node as the root contains the node to be searched. We still need to traverse it. The second traversal operation was actually done before the first traversal. Due to repeated traversal, the time efficiency of this method is definitely not the best.
We mentioned earlier that if there is a pointer to the parent node in the node, we can convert the problem to the common node of the two linked lists. Now we can find a way to get this linked list. We analyzed how to get a path starting with the root node in question 4th of this series. Here we can make a slight change:
// Get the path form pHead and pNode in a tree with head pHead
bool GetNodePath(TreeNode* pHead, TreeNode* pNode, std::list<TreeNode*>& path)
{
if(pHead == pNode) return true;
path.push_back(pHead);
bool found = false;
if(pHead->m_pLeft != NULL)
found = GetNodePath(pHead->m_pLeft, pNode, path);
if(!found && pHead->m_pRight)
found = GetNodePath(pHead->m_pRight, pNode, path);
if(!found)
path.pop_back();
return found;
}
Since the path starts with the node, the lowest common parent node is the last common node in the path:
// Get the last common Node in two lists: path1 and path2
TreeNode* LastCommonNode ( const std::list<TreeNode*>& path1, const std::list<TreeNode*>& path2 )
{
std::list<TreeNode*>::const_iterator iterator1 = path1.begin();
std::list<TreeNode*>::const_iterator iterator2 = path2.begin();
TreeNode* pLast = NULL;
while(iterator1 != path1.end() && iterator2 != path2.end())
{
if(*iterator1 == *iterator2)
pLast = *iterator1;
iterator1++;
iterator2++;
}
return pLast;
}
With the first two subfunctions, it is easy to find the lowest common parent node of the two nodes. We first find the two paths from the root node to the two nodes, and then find the last common node of the two paths. The Code is as follows:
// Find the last parent of pNode1 and pNode2 in a tree with head pHead
TreeNode* LastCommonParent_2(TreeNode* pHead, TreeNode* pNode1, TreeNode* pNode2)
{
if(pHead == NULL || pNode1 == NULL || pNode2 == NULL)
return NULL;
std::list<TreeNode*> path1;
GetNodePath(pHead, pNode1, path1);
std::list<TreeNode*> path2;
GetNodePath(pHead, pNode2, path2);
return LastCommonNode(path1, path2);
}
The time complexity of this approach is O (n), and the time efficiency is much better than the first method. However, we should also note that this idea requires two linked lists to store the path, which is less efficient than the first method.