这个题目有两个变种:有回溯指针的二叉树和无回溯指针的二叉树
题目:
给出两个节点node1,node2和二叉树根节点root,查找node1和node2的最近的公共祖先节点;特殊值(简易)之处在于每个节点都有一个指向父节点的回溯指针
解法一:最常规的解法--依次轮询
思路:
使用回溯指针可以找到node1和node2到root的路径,在这两条路径上总有一个汇聚点,通过轮询可以找到
评价:
这是最通俗易懂的方法,但是效率很低o(n^2),n为树的高度
代码:
/*
寻找 两个节点的最近公共祖先节点
*/
BitTree NCA(BitTree *root, BitTree *node1, BitTree *node2)
{
if(!root) return null;
BitTree *tmp;
while(node1)
{
tmp = node2;
while(tmp)
{
if(node1 == tmp) return node1;
tmp = tmp->parent;
}
node1 = node1->parent();
}
}
解法二:巧妙转化
思路:
第一种方式是倒序的,找到第一个相同的节点就是NCA;但是效率太差。
其实我们可以两次轮询后保存下node1和node2到root的路径,然后从头开始轮询两条路径,也可以很快找到,而且复杂度是o(3n),n是树的高度
评价:
巧妙的转化为了求两个线性表的第一个不同节点。
代码:
/*
获取两个节点的最近公共祖先节点
*/
BitTree * NCA(BitTree *root, BitTree *node1, BitTree *node2)
{
BitTree *tmp;
int len1 = 0, len2 = 0;
int path1[100] = {0}, path2[100] = {0};
if(!root) return null;
while(node1->parent)
{
tmp = node1;
node1 = node1->parent;
if(node1->left == tmp){path1[len1++] = 1;}
else{path1[len1++] = 0;}
}
while(node2->parent)
{
tmp = node2;
node2 = node2->parent;
if(node2->left == tmp){path2[len2++] = 1;}
else{path2[len2++] = 0;}
}
for(; len1 > 0 && len2 > 0;)
{
if(path1[--len1] != path2[--len2])
{
return root;
}
root = (path1[len1] == 1? root->left:root->right);
}
}
解法二:哈希高效
思路:
哈希表的特性就是value可以直接定位到位置,而这里我们要做的无非就是看看两条路径的最近交叉点。所以可以先依次将node1到root的路径放入哈希表;然后轮询node2到root的路径,当第一次出现存在的key时则找到了NCA。
评价:
善于利用哈希表,尤其是当需要比较两个表中的元素异同的时候(之前的判断字母完全包含也是利用了这一点)
代码:
/*
对于hashtable的用法,不会,需要学习。
获取两个节点的最近公共祖先节点
*/
BitTree * NCA(BitTree *root, BitTree *node1, BitTree *node2)
{
if(!root) return null;
hashTable<BitTree> h;
while(node1)
{
h.push(node1);
node1 = node1->parent();
}
while(node2)
{
if(h.exist(node2)) return node2;
node2 = node2->parent();
}
}