#include <bits/stdc++.h>
using namespace std;
class Employee {
public:
int employeeId;
string employeeName;
Employee(int id, string name){
employeeId = id;
employeeName = name;
}
};
// Each GroupNode can have subgroups and a set of employees.
class GroupNode {
public:
string groupName;
vector<GroupNode*> subGroups;
unordered_set<string> employeesInGroup; // ordering doesn't matter here
GroupNode(string name){
groupName = name;
}
void addSubGroup(GroupNode* subGroup) {
subGroups.push_back(subGroup);
}
void addEmployee(string employeeName) {
employeesInGroup.insert(employeeName);
}
};
// n = number of groups/nodes,
// m = number of target employees.
// h = height of tree
// The algorithm performs a depth-first traversal of the company
// In the worst case, it may need to visit each node of the company. If there are n groups/nodes, the traversal complexity is O(n)
// and for each group/node visited, we iterate over targetEmployees vector which takes O(m)
// So Time = O(n*m)
// Space complexity of the recursive approach is determined by the maximum depth of the recursion stack.
// In the worst-case scenario, this depth is equal to the height of the tree h.
// For a balanced tree, h =(log n), but in the worst case (e.g., a skewed tree), h = n
// Space = O(h)
GroupNode* findLCArecursion(GroupNode* root, vector<string> targetEmployees) {
// Base Case , lets say companyRoot is null i.e empty company
if (root == nullptr) {
return nullptr;
}
// Check if current group contains any of the target employees
for (int i = 0; i < targetEmployees.size(); i++) { // O(m)
if (root->employeesInGroup.find(targetEmployees[i]) != root->employeesInGroup.end()) {
return root; // found potential LCA
}
}
int foundCount = 0;
GroupNode* ans = nullptr;
// Recurse through subgroups to find the LCA
for (int i = 0; i < root->subGroups.size(); i++) {
GroupNode* subLCA = findLCArecursion(root->subGroups[i], targetEmployees);
if (subLCA != nullptr) {
foundCount++;
ans = subLCA;
}
}
// If more than one subgroup contains any of the target employees,
// the current node is the lowest common ancestor
if (foundCount > 1) {
return root;
}
// Otherwise, return the subgroup that contains any of the target employees
return ans;
}
// Lowest Common Ancestor
GroupNode* findLCA(GroupNode* root, vector<string> targetEmployees) {
if (targetEmployees.empty()) return nullptr;
return findLCArecursion(root, targetEmployees);
}
int main() {
/*
- Company
/ \
HR Engg
/ \ / \
Mona Springs BE FE
/ \ / \
Alice Bob Lisa Marley
*/
GroupNode* companyRootNode = new GroupNode("Company");
GroupNode* hr = new GroupNode("HR");
GroupNode* engg = new GroupNode("Engg");
GroupNode* be = new GroupNode("BE");
GroupNode* fe = new GroupNode("FE");
companyRootNode->addSubGroup(hr);
companyRootNode->addSubGroup(engg);
engg->addSubGroup(be);
engg->addSubGroup(fe);
hr->addEmployee("Mona");
hr->addEmployee("Springs");
be->addEmployee("Alice");
be->addEmployee("Bob");
fe->addEmployee("Lisa");
fe->addEmployee("Marley");
// Test cases
vector<string> employees1 = {"Lisa", "Marley"};
GroupNode* result = findLCA(companyRootNode, employees1);
if(result == nullptr) {
cout << "No common parent"<<endl;
} else {
cout << result->groupName<<endl;
}
vector<string> employees2 = {"Alice", "Marley"};
GroupNode* result2 = findLCA(companyRootNode ,employees2);
if(result == nullptr) {
cout << "No common parent, there are isolated trees in the company"<<endl;
} else {
cout << result2->groupName<<endl;
}
vector<string> employees3 = {"Mona", "Lisa", "Bob"};
GroupNode* result3 = findLCA(companyRootNode ,employees3);
if(result == nullptr) {
cout << "No common parent"<<endl;
} else {
cout << result3->groupName<<endl;
}
return 0;
}