当前位置:网站首页>返回二叉树中两个节点的最低公共祖先
返回二叉树中两个节点的最低公共祖先
2022-07-05 02:31:00 【明朗晨光】
1、题目
给定一棵二叉树的根节点 root
,和另外两个节点 a
和 b
,返回 a
和 b
的最低公共祖先。
2、分析
最低公共祖先:两个节点往上走,到哪个节点汇聚,该节点就是最低公共祖先。对于一个节点是根,另一个节点是其子树上的节点的情况,该根节点就是最低公共祖先。
方法一:遍历二叉树,利用哈希表记录每个节点的父节点,将节点 a
沿途的到根节点的所有节点都放入一个集合中,然后找到从 b
开始沿途一直到根节点有什么节点在集合中,则它就是最低公共祖先。
方法二:二叉树递归套路。
- 目标:给定 a 和 b 节点,在 x 为根的这棵二叉树上,找到 a 和 b 最开始汇聚的节点。如果没有汇聚,则返回空,否则返回汇聚的节点。
- 可能性:是否发现了 a 节点,是否发现了 b 节点。
分为两类:- 汇聚点和 x 无关(x 不是最低汇聚点)
① a 和 b 在左树上汇聚了;
② a 和 b 在右树上汇聚了;
③ a 和 b 不全,即该树上只有其中一个节点或者没有。 - 汇聚点和 x 有关(x就是最低汇聚点)
① 左树发现了 a 和 b 其中一个,右树发现了另一个,二者在 x 处汇聚;
② x 本身就是 a 节点,然后在左树或者右树发现了 b 节点;
③ x 本身就是 b 节点,然后在左树或者右树发现了 a 节点;
- 汇聚点和 x 无关(x 不是最低汇聚点)
总结来说,向左树和右树需要搜集的信息:(1)在树上是否发现了 a;(2)在树上是否发现了 b;(3)a 和 b 汇聚的最低公共祖先。
收集左树和右树的信息,然后更新当前节点的信息,这就是一个后序遍历,时间复杂度 O ( n ) O(n) O(n)。
3、实现
C++ 版
/************************************************************************* > File Name: 040.返回二叉树两个节点的最低公共祖先.cpp > Author: Maureen > Mail: [email protected] > Created Time: 一 7/ 4 13:42:23 2022 ************************************************************************/
#include <iostream>
#include <ctime>
#include <unordered_map>
#include <set>
#include <vector>
using namespace std;
class TreeNode {
public:
int value;
TreeNode *left;
TreeNode *right;
TreeNode(int data) : value(data) {
}
};
//方法一:建表保存父节点信息
void fillParentMap(TreeNode *root, unordered_map<TreeNode*, TreeNode*> &parentMap) {
if (root->left != nullptr) {
parentMap[root->left] = root;
fillParentMap(root->left, parentMap);
}
if (root->right != nullptr) {
parentMap[root->right] = root;
fillParentMap(root->right, parentMap);
}
}
TreeNode *lowestAncestor1(TreeNode *root, TreeNode *o1, TreeNode *o2) {
if (root == nullptr) return nullptr;
unordered_map<TreeNode*, TreeNode*> parentMap;
parentMap[root] = nullptr;
fillParentMap(root, parentMap);//保存每个节点的父节点
set<TreeNode*> o1set;
TreeNode *cur = o1;
//o1节点沿途到根节点的所有节点放入o1set中
while (cur != nullptr) {
o1set.insert(cur);
cur = parentMap[cur];
}
cur = o2;
while (!o1set.count(cur)) {
cur = parentMap[cur];
}
return cur;
}
//方法二:二叉树递归套路
class Info {
public:
bool existA;
bool existB;
TreeNode *ancestor;
Info(bool a, bool b, TreeNode *node) : existA(a), existB(b), ancestor(node) {
}
};
Info *process(TreeNode *x, TreeNode *a, TreeNode *b) {
if (x == nullptr) {
return new Info(false, false, nullptr);
}
Info *leftInfo = process(x->left, a, b);
Info *rightInfo = process(x->right, a, b);
bool existA = (x == a) || leftInfo->existA || rightInfo->existA;
bool existB = (x == b) || leftInfo->existB || rightInfo->existB;
TreeNode *ancestor = nullptr;
if (leftInfo->ancestor != nullptr) {
ancestor = leftInfo->ancestor;
} else if(rightInfo->ancestor != nullptr) {
ancestor = rightInfo->ancestor;
} else {
if (existA && existB) ancestor = x;
}
return new Info(existA, existB, ancestor);
}
TreeNode *lowestAncestor2(TreeNode *root, TreeNode *a, TreeNode *b) {
return process(root, a, b)->ancestor;
}
//for test
//for test
TreeNode *generate(int level, int maxl, int maxv) {
if (level > maxl || (rand() % 100 / (double)101) < 0.5)
return nullptr;
TreeNode *root = new TreeNode(rand() % maxv);
root->left = generate(level + 1, maxl, maxv);
root->right = generate(level + 1, maxl, maxv);
return root;
}
TreeNode *generateRandomBST(int level, int value) {
return generate(1, level, value);
}
void fillPrelist(TreeNode *root, vector<TreeNode*> &arr) {
if (root == nullptr) return ;
arr.push_back(root);
fillPrelist(root->left, arr);
fillPrelist(root->right, arr);
}
TreeNode *pickRandomOne(TreeNode *root) {
if (root == nullptr) return nullptr;
vector<TreeNode*> arr;
fillPrelist(root, arr);
int randInd = rand() % arr.size();
return arr[randInd];
}
int main() {
srand(time(0));
int maxLevel = 4;
int maxValue = 100;
int testTimes = 1000001;
cout << "Test begin:" << endl;
for (int i = 0; i < testTimes; i++) {
TreeNode *root = generateRandomBST(maxLevel, maxValue);
TreeNode *a = pickRandomOne(root);
TreeNode *b = pickRandomOne(root);
if (lowestAncestor1(root, a, b) != lowestAncestor2(root, a, b)) {
cout << "Failed!" << endl;
return 0;
}
if (i && i % 1000 == 0) cout << i << " cases passed!" << endl;
}
cout << "Success!!" << endl;
return 0;
}
Java 版
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
public class LowestAncestor{
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
//方法一:建表,保存父节点信息
public static Node lowestAncestor1(Node head, Node o1, Node o2) {
if (head == null) {
return null;
}
// key的父节点是value
HashMap<Node, Node> parentMap = new HashMap<>();
parentMap.put(head, null);
fillParentMap(head, parentMap);
HashSet<Node> o1Set = new HashSet<>();
Node cur = o1;
o1Set.add(cur);
while (parentMap.get(cur) != null) {
cur = parentMap.get(cur);
o1Set.add(cur);
}
cur = o2;
while (!o1Set.contains(cur)) {
cur = parentMap.get(cur);
}
return cur;
}
public static void fillParentMap(Node head, HashMap<Node, Node> parentMap) {
if (head.left != null) {
parentMap.put(head.left, head);
fillParentMap(head.left, parentMap);
}
if (head.right != null) {
parentMap.put(head.right, head);
fillParentMap(head.right, parentMap);
}
}
//方法二:二叉树递归套路
public static Node lowestAncestor2(Node head, Node a, Node b) {
return process(head, a, b).ans;
}
public static class Info {
public boolean findA; //是否找到a
public boolean findB; //是否找到b
public Node ans; //是否找到汇聚点
public Info(boolean fa, boolean fb, Node an) {
findA = fa;
findB = fb;
ans = an;
}
}
public static Info process(Node x, Node a, Node b) {
if (x == null) {
//设置空树
return new Info(false, false, null);
}
Info leftInfo = process(x.left, a, b);
Info rightInfo = process(x.right, a, b);
//发现a和b
boolean findA = (x == a) || leftInfo.findA || rightInfo.findA;
boolean findB = (x == b) || leftInfo.findB || rightInfo.findB;
Node ans = null;
if (leftInfo.ans != null) {
//左树上找到最初汇聚点
ans = leftInfo.ans;
} else if (rightInfo.ans != null) {
//右树上找到最初汇聚点
ans = rightInfo.ans;
} else {
//左树和右树都没有找到汇聚点
if (findA && findB) {
//如果发现了a和b,那么汇聚点一定就是x
ans = x;
}
}
return new Info(findA, findB, ans);
}
// for test
public static Node generateRandomBST(int maxLevel, int maxValue) {
return generate(1, maxLevel, maxValue);
}
// for test
public static Node generate(int level, int maxLevel, int maxValue) {
if (level > maxLevel || Math.random() < 0.5) {
return null;
}
Node head = new Node((int) (Math.random() * maxValue));
head.left = generate(level + 1, maxLevel, maxValue);
head.right = generate(level + 1, maxLevel, maxValue);
return head;
}
// for test
public static Node pickRandomOne(Node head) {
if (head == null) {
return null;
}
ArrayList<Node> arr = new ArrayList<>();
fillPrelist(head, arr);
int randomIndex = (int) (Math.random() * arr.size());
return arr.get(randomIndex);
}
// for test
public static void fillPrelist(Node head, ArrayList<Node> arr) {
if (head == null) {
return;
}
arr.add(head);
fillPrelist(head.left, arr);
fillPrelist(head.right, arr);
}
public static void main(String[] args) {
int maxLevel = 4;
int maxValue = 100;
int testTimes = 1000000;
for (int i = 0; i < testTimes; i++) {
Node head = generateRandomBST(maxLevel, maxValue);
Node o1 = pickRandomOne(head);
Node o2 = pickRandomOne(head);
if (lowestAncestor1(head, o1, o2) != lowestAncestor2(head, o1, o2)) {
System.out.println("Oops!");
}
}
System.out.println("finish!");
}
}
边栏推荐
- Asynchronous and promise
- Write a thread pool by hand, and take you to learn the implementation principle of ThreadPoolExecutor thread pool
- Practical case of SQL optimization: speed up your database
- 【LeetCode】222. The number of nodes of a complete binary tree (2 mistakes)
- Android advanced interview question record in 2022
- 数据库和充值都没有了
- 【附源码】基于知识图谱的智能推荐系统-Sylvie小兔
- ASP. Net core 6 framework unveiling example demonstration [01]: initial programming experience
- Serious bugs with lifted/nullable conversions from int, allowing conversion from decimal
- He was laid off.. 39 year old Ali P9, saved 150million
猜你喜欢
The most powerful new household god card of Bank of communications. Apply to earn 2100 yuan. Hurry up if you haven't applied!
如何做一个炫酷的墨水屏电子钟?
Write a thread pool by hand, and take you to learn the implementation principle of ThreadPoolExecutor thread pool
openresty ngx_lua执行阶段
Hmi-31- [motion mode] solve the problem of picture display of music module
Summary and practice of knowledge map construction technology
【LeetCode】106. Construct binary tree from middle order and post order traversal sequence (wrong question 2)
Openresty ngx Lua Execution stage
Zabbix
Binary tree traversal - middle order traversal (golang)
随机推荐
[download white paper] does your customer relationship management (CRM) really "manage" customers?
Talk about the things that must be paid attention to when interviewing programmers
Tucson will lose more than $400million in the next year
Asynchronous and promise
Kotlin - coroutine
The MySQL team development specifications used by various factories are too detailed. It is recommended to collect them!
The database and recharge are gone
Bumblebee: build, deliver, and run ebpf programs smoothly like silk
Security level
How to build a technical team that will bring down the company?
使用druid连接MySQL数据库报类型错误
When to catch an exception and when to throw an exception- When to catch the Exception vs When to throw the Exceptions?
【LeetCode】98. Verify the binary search tree (2 brushes of wrong questions)
Pytest (4) - test case execution sequence
The steering wheel can be turned for one and a half turns. Is there any difference between it and two turns
. Net starts again happy 20th birthday
Yuan universe also "real estate"? Multiple second-hand trading websites block metauniverse keywords
Chinese natural language processing, medical, legal and other public data sets, sorting and sharing
2022/02/13
RichView TRVStyle MainRVStyle