查找二叉树子节点的最近共同父节点
分析
实现
算法复杂度
其他算法
题目升级
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
实例1
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
实例2
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉搜索树中。
分析
对于二叉树来讲,由于左右子树指针的存在,使得正常情况下的自上而下遍历显得比较简单,而下而上的查找并不那么容易,所以一种直观的思维就是从根节点开始遍历,直到找到节点p pp,记录路径数组为p a t h _ p path\_ppath_p,同理找到根节点到节点q qq的路径数组p a t h _ q path\_qpath_q,只要能够找到两个路径组中最到的i n d e x indexindex,使得p a t h _ p [ i n d e x ] = = p a t h _ q [ i n d e x ] path\_p[index]==path\_q[index]path_p[index]==path_q[index],则i n d e x indexindex对应的节点即为最近共同父节点。
实现
基于上述思考,尝试使用数组来进行路径存储。
struct Path {
int capacity; //容量
int occupy; // 实际占用量
struct TreeNode **result;// 存储的路径节点
};
void addElement(struct Path *path, struct TreeNode* node) {
if (path->occupy == path->capacity) {
// 扩容
path->capacity *= 2;
path->result = realloc(path->result, sizeof(struct TreeNode *) * path->capacity);
}
path->result[path->occupy++] = node;
}
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q){
struct Path *path_p = malloc(sizeof(struct Path));
path_p->capacity = 50;
path_p->occupy = 0;
int size = sizeof(struct TreeNode *) * path_p->capacity;
path_p->result = malloc(size);
memset(path_p->result, 0 , size);
struct TreeNode *current = root;
while(current->val != p->val) {
addElement(path_p, current);
if (current->val > p->val) {
current = current -> left;
} else {
current = current -> right;
}
}
addElement(path_p, current);
current = root;
struct Path *path_q = malloc(sizeof(struct Path));
path_q->capacity = 50;
path_q->occupy = 0;
size = sizeof(struct TreeNode *) * path_q->capacity;
path_q->result = malloc(size);
memset(path_q->result, 0 , size);
while(current->val != q->val) {
addElement(path_q, current);
if (current->val > q->val) {
current = current -> left;
} else {
current = current -> right;
}
}
addElement(path_q, current);
struct TreeNode *node = NULL;
for(int pIndex = path_p->occupy - 1; pIndex >= 0; pIndex--) {
for(int qIndex = path_q->occupy - 1; qIndex >= 0; qIndex--) {
if(path_p->result[pIndex] == path_q->result[qIndex]) {
return path_p->result[pIndex];
}
}
}
return NULL;
}
算法复杂度
时间复杂度:最坏的情况下,二叉搜索树变成了一个类似于链表的结构,而p , q p,qp,q是在最底端的两个节点那么搜索p , q p,qp,q节点的时间复杂度都可以达到n nn(n nn为树中节点个数),时间复杂度为O ( n ) O(n)O(n);
空间复杂度:同样最坏的情况下,需要使用开辟跟节点数相同的数组空间来存储节点路径,所以空间复杂度也为O ( n ) O(n)O(n).
其他算法
对于上述算法来讲需要遍历两次树结构来获取跟节点到指定节点的路径,然后倒叙获取路径数组中第一个相同节点即可最近父节点.但事实上,可以尝试将两次查找合并在一起,对于当前节点c u r r e n t currentcurrent,无非有三种情况:
current->val > p->val && current->val > q->val,则说明p,q均在current的左子树上,此时取current = current->left;
current->val < p->val && current->val < q->val,则说明p,q均在current的右子树上,此时取current = current->right;
最后一种情况,要么current就是p或者q节点之一,要么p,q分别在current的左右子树上.也就是要查找的最近父节点。实现起来发给这个样子:
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
struct TreeNode* ancestor = root;
while (true) {
if (p->val < ancestor->val && q->val < ancestor->val) {
ancestor = ancestor->left;
} else if (p->val > ancestor->val && q->val > ancestor->val) {
ancestor = ancestor->right;
} else {
return ancesto
}
}
return NULL;
}
这样就可以将时间复杂度降低为O ( n ) O(n)O(n),同时空间复杂度也将为常数O ( 1 ) O(1)O(1).
题目升级
如果题目中的树只是一颗普通的二叉树,那么最近父节点该怎么查找?
其实尝试将结果分类,会发现无外乎以下情况:
p,q结点分布在当前结点两侧或者当前结点就是p或者q之一,那么根结点就是最近父节点;
p,q结点在当前结点的左子树上,那么最近父结点肯定是第一个查询到的p或者q;
p,q结点分布在当前结点右子树上,那么那么最近父结点肯定是第一个查询到的p或者q;
这样就可以使用递归进行查找:
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
if (!root) return NULL;
if (root->val == p->val || root->val == q->val ) return root;
struct TreeNode *left = lowestCommonAncestor(root->left, p, q);
struct TreeNode *right = lowestCommonAncestor(root->right, p, q);
if (left && right) return root;
return left ? left : right;
}
同样最坏的情况是,二叉树退化成了一个类似于单链表的结构,p,q两个节点就在表的末端最后两个节点,这样的话,时间复杂度也会变为O ( n ) O(n)O(n);不消耗额外的空间。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。