此题大概分为3种情况:
1、节点中无parent,但提供了root
(1)、此种情况又分为两种,开辟空间,使用容器来保存路径,将其转换为求链表公共节点的问题,时间复杂度为O(N),空间复杂度为O(N)
(2)、不开辟空间,在节点的左右子树上寻找两个节点,若两个节点存在在节点的左右子树,则该节点为最近的公共节点,否则继续在该节点的左右子树寻找,主要使用递归来完成寻找,时间复杂度为O(N*N)
综合分析,推荐使用第一种方法
2、节点中有parent,但未提供root
3、此二叉树是二叉搜索树
解决方法:
前面两种情况都是想办法将此题转换为求解两个相交链表的交点:
第一种情况需要我们将两个节点到root的路径保存起来,此时路径就好像是链表;
第二种情况比第一种情况还要简单些,我们只需要不断向前找,直至parent为空,统计两个节点的深度,此时依旧是将两个节点看做两个链表的起点,依旧是相交链表的问题;
第三种情况:我们只需由根节点开始不断和两个节点的值做对比,若节点值均大于两个节点的值,则在左子树继续寻找,若节点均小于两个节点的值,则在右子树继续寻找,若节点值恰好在两个节点之间,则此节点就是最近的公共祖先。
三种方法均写在一个类中了,节点共用,但三种方法对节点的使用时按照三种条件严格区分的,代码如下:
#pragma once
#include<iostream>
#include<stack>
using namespace std;
//Lowest Common Ancestor最小公共节点问题,简称LCA
//三种情况,普通二叉树,无parent,已知root;有parent,无root;二叉搜索树
template<class T>
struct BSNode
{
int key;
BSNode* left;
BSNode* right;
BSNode* parent;
BSNode(T value):
key(value)
, left(NULL)
, right(NULL)
, parent(NULL)
{}
};
template<class T>
class BSTree
{
typedef BSNode<T> Node;
public:
BSTree():
_root(NULL)
{}
~BSTree()
{}
public:
void Insert(T key)
{
Node* newnode = new Node(key);
if (_root == NULL)
{
_root = newnode;
}
else
{
Node* cur = _root;
Node* parent = NULL;
while (cur)
{
parent = cur;
if (key > cur->key)
{
cur = cur->right;
}
else if (key < cur->key)
{
cur = cur->left;
}
else
{
return;
}
}
if (parent->key > key)
{
parent->left = newnode;
}
else
{
parent->right = newnode;
}
newnode->parent = parent;
}
}
void Print()
{
if (_root == NULL)
{
return;
}
_Print(_root);
}
void _Print(Node* cur)
{
if (cur == NULL)
{
return;
}
else if (cur->left)
{
_Print(cur->left);
}
cout << cur->key << " ";
if (cur->right)
{
_Print(cur->right);
}
}
Node* Find(int key)
{
Node* cur = _root;
while (true)
{
if (cur->key > key)
{
cur = cur->left;
}
else if (cur->key < key)
{
cur = cur->right;
}
else
{
return cur;
}
}
return NULL;
}
// 1、普通二叉树,第一种方法,使用容器来保存路径
//(1)、由根节点寻找给定的两个节点,用栈保存路径节点的值
//(2)、此时问题转换为求两个链表的公共节点问题,将较长的
void Path(Node* root, Node* node,stack<Node*> &s,bool &f)
{
s.push(root);
if (root == node)
{
f = false;
return;
}
if (f && root->left != NULL)
{
Path(root->left, node, s,f);
}
if (f && root->right != NULL)
{
Path(root->right, node, s,f);
}
if (f)
{
s.pop();
}
}
Node* FindLCA1(Node* node1, Node* node2)
{
if (node1 == NULL || node2 == NULL || _root == NULL)
{
return NULL;
}
stack<Node*> s1;
stack<Node*> s2;
bool f = true;
Path(_root, node1, s1,f);
f = true;
Path(_root, node2, s2,f);
//将两个栈调整为长度一样的
int count = 0;
if (s1.size() > s2.size())
{
count = s1.size() - s2.size();
while (count--)
{
s1.pop();
}
}
else
{
count = s2.size() - s1.size();
while (count--)
{
s2.pop();
}
}
//逐个比较,寻找相同节点
while (s1.top() != s2.top())
{
s1.pop();
s2.pop();
}
Node* LCA = s1.top();
return LCA;
}
//第一类第二种方法:普通二叉树无需开辟空间的方法,递归在节点左右子树查找两个节点是否分别在左右子树中,若存在,则该节点是最近公共祖先,否则递归左右子树继续查找
//1、从根节点开始在左右子树上查找两个节点是否存在
//2、若存在,则该节点就是最近公共祖先,否则继续在该节点的左右子树继续查找
Node* FindR(Node* root, Node* node)
{
if (root == NULL || node == NULL)
{
return NULL;
}
while (root!=NULL)
{
if (root->key > node->key)
{
root = root->left;
}
else if (root->key < node->key)
{
root = root->right;
}
else
{
return root;
}
}
return NULL;
}
Node* FindLCA4R(Node* root, Node* node1, Node* node2)
{
if (root == NULL)
{
return NULL;
}
else if (FindR(root->left, node1) && FindR(root->right, node2))
{
return root;
}
if (FindLCA4R(root->left, node1, node2) == NULL)
{
FindLCA4R(root->right, node1, node2);
}
}
Node* FindLCA4(Node* node1, Node* node2)
{
if (_root == NULL || node1 == NULL || node2 == NULL)
{
return NULL;
}
else if (node1 == _root || node2 == _root)
{
return _root;
}
return FindLCA4R(_root, node1, node2);
}
//第三种情况,二叉搜索树,充分利用二叉搜索树的特性
//(1)、由根节点开始逐个和两个节点的key值对比
//(2)、若均大于,则在该节点的左子树;若均小于,则在该节点的右子树;若此时节点的值恰好在两个节点的中间,则此节点必然为最小公共节点
Node* FindLCA3(Node* node1, Node* node2)
{
if (_root == NULL || node1 == NULL || node2 == NULL)
{
return NULL;
}
BSNode<T>* cur = _root;
while (true)
{
if (cur->key > node1->key && cur->key > node2->key)
{
cur = cur->left;
}
else if (cur->key < node1->key && cur->key < node2->key)
{
cur = cur->right;
}
else
{
return cur;
}
}
return NULL;
}
//第二种,有parent,但无root(用当前节点向前找,直至父节点为NULL)
//(1)、有两个节点向前找,计算出两个节点的深度
//(2)、此时问题和第一种情况类似
Node* FindLCA2(Node* node1, Node* node2)
{
if (node1 == NULL || node2 == NULL)
{
return NULL;
}
int node1Len = 0;
int node2Len = 0;
Node* cur = node1;
while (cur)
{
++node1Len;
cur = cur->parent;
}
cur = node2;
while (cur)
{
++node2Len;
cur = cur->parent;
}
//调整节点
int count = 0;
if (node1Len > node2Len)
{
count = node1Len - node2Len;
while (count--)
{
node1 = node1->parent;
}
}
else
{
count = node2Len - node1Len;
while (count--)
{
node2 = node2->parent;
}
}
//由下向上寻找相同的节点
while (node1 != node2)
{
node1 = node1->parent;
node2 = node2->parent;
}
return node1;
}
private:
BSNode<T>* _root;
};