How to convert a series of parent-child relationships into a hierarchical tree?

Free Coding Questions Catalog
Boost your coding skills with our essential coding questions catalog. Take a step towards a better tech career now!

Converting Parent-Child Relationships into a Hierarchical Tree

Transforming a flat list of parent-child relationships into a hierarchical tree structure is a common task in various applications, such as organizational charts, file systems, and category classifications. This process involves organizing data in a way that reflects the inherent hierarchy, making it easier to traverse, visualize, and manage.

Understanding the Parent-Child Relationship

A parent-child relationship defines how elements (children) are connected to each other through a common ancestor (parent). For example, in an organizational chart:

  • Parent: CEO
  • Children: CTO, CFO

Each child can, in turn, have its own children, creating multiple levels of hierarchy.

Representing Parent-Child Data

Parent-child relationships can be represented in various formats, such as:

  1. List of Tuples/Pairs:

    relationships = [ (1, 2), # Parent ID 1 has Child ID 2 (1, 3), (2, 4), (2, 5), (3, 6) ]
  2. List of Dictionaries:

    relationships = [ {'parent': 1, 'child': 2}, {'parent': 1, 'child': 3}, {'parent': 2, 'child': 4}, {'parent': 2, 'child': 5}, {'parent': 3, 'child': 6} ]
  3. Database Tables:

    • Employees Table:
      EmployeeIDEmployeeNameManagerID
      1CEONULL
      2CTO1
      3CFO1
      4Dev Manager2
      5Developer2
      6Accountant3

Steps to Convert Parent-Child Relationships into a Hierarchical Tree

We'll demonstrate the conversion using Python, leveraging dictionaries to represent nodes and their children. However, the concepts apply to other programming languages as well.

Step 1: Define the Data Structure

First, define the parent-child relationships. We'll use a list of tuples for simplicity.

relationships = [ (1, 2), # Parent ID 1 has Child ID 2 (1, 3), (2, 4), (2, 5), (3, 6) ]

Step 2: Create Node Class (Optional)

For better clarity and structure, especially in more complex trees, define a Node class.

class Node: def __init__(self, id, name=None): self.id = id self.name = name # Optional: Store additional data self.children = [] def __repr__(self): return f"Node(id={self.id}, children={self.children})"

Step 3: Build the Tree

Here's a step-by-step approach to building the tree:

  1. Create a Dictionary of Nodes: Map each unique ID to a Node instance.

  2. Assign Children to Parents: Iterate through the relationships and assign each child to its respective parent.

  3. Identify the Root(s): Nodes without a parent are considered roots.

Implementation:
def build_tree(relationships): nodes = {} children_ids = set() # Create all nodes for parent_id, child_id in relationships: if parent_id not in nodes: nodes[parent_id] = Node(parent_id) if child_id not in nodes: nodes[child_id] = Node(child_id) # Assign child to parent nodes[parent_id].children.append(nodes[child_id]) children_ids.add(child_id) # Identify root nodes (nodes that are not children) root_ids = set(nodes.keys()) - children_ids roots = [nodes[root_id] for root_id in root_ids] return roots # Example usage tree_roots = build_tree(relationships) for root in tree_roots: print(root)

Output:

Node(id=1, children=[Node(id=2, children=[Node(id=4, children=[]), Node(id=5, children=[])]), Node(id=3, children=[Node(id=6, children=[])])])

This output represents the hierarchical tree with Node 1 as the root, Nodes 2 and 3 as its children, and so on.

Step 4: Traversing the Tree (Optional)

To visualize or process the tree further, implement traversal methods like Depth-First Search (DFS) or Breadth-First Search (BFS).

Example: Pre-Order Traversal
def pre_order_traversal(node, level=0): print(' ' * level + f"Node ID: {node.id}") for child in node.children: pre_order_traversal(child, level + 1) # Traverse each tree root for root in tree_roots: pre_order_traversal(root)

Output:

Node ID: 1
  Node ID: 2
    Node ID: 4
    Node ID: 5
  Node ID: 3
    Node ID: 6

Handling Multiple Roots and Edge Cases

  1. Multiple Roots: The provided build_tree function can handle multiple roots. Each root will be a separate tree in the roots list.

  2. Cyclic Relationships: Ensure that the data does not contain cycles, as recursion-based tree building can lead to infinite loops. Implement checks if necessary.

  3. Missing Parents: If a child references a non-existent parent, decide how to handle it—either by creating the missing parent or flagging an error.

Alternative Approach: Using Dictionaries

Instead of defining a Node class, you can represent the tree using nested dictionaries for simpler structures.

def build_tree_dict(relationships): tree = {} children = {} for parent, child in relationships: if parent not in tree: tree[parent] = {} if parent not in children: children[parent] = [] children[parent].append(child) if child not in tree: tree[child] = {} # Find root nodes all_children = set(child for _, child in relationships) roots = set(tree.keys()) - all_children def build_subtree(node): return {node: [build_subtree(child) for child in children.get(node, [])]} hierarchical_tree = {root: [build_subtree(child) for child in children.get(root, [])] for root in roots} return hierarchical_tree # Example usage tree_dict = build_tree_dict(relationships) print(tree_dict)

Output:

{ 1: [ {2: [ {4: []}, {5: []} ]}, {3: [ {6: []} ]} ] }

Example in JavaScript

For those using JavaScript, here's how you can achieve the same:

class Node { constructor(id, name = null) { this.id = id; this.name = name; this.children = []; } } function buildTree(relationships) { const nodes = {}; const childrenIds = new Set(); // Create all nodes relationships.forEach(([parentId, childId]) => { if (!nodes[parentId]) { nodes[parentId] = new Node(parentId); } if (!nodes[childId]) { nodes[childId] = new Node(childId); } // Assign child to parent nodes[parentId].children.push(nodes[childId]); childrenIds.add(childId); }); // Identify root nodes const rootIds = Object.keys(nodes).filter(id => !childrenIds.has(parseInt(id))); const roots = rootIds.map(id => nodes[id]); return roots; } // Example usage const relationships = [ [1, 2], [1, 3], [2, 4], [2, 5], [3, 6] ]; const treeRoots = buildTree(relationships); console.log(JSON.stringify(treeRoots, null, 2));

Output:

[ { "id": 1, "name": null, "children": [ { "id": 2, "name": null, "children": [ { "id": 4, "name": null, "children": [] }, { "id": 5, "name": null, "children": [] } ] }, { "id": 3, "name": null, "children": [ { "id": 6, "name": null, "children": [] } ] } ] } ]

Conclusion

Converting parent-child relationships into a hierarchical tree involves:

  1. Representing the Relationships: Using lists of tuples, dictionaries, or database tables.
  2. Building Nodes: Creating node instances or using nested dictionaries.
  3. Assigning Children: Linking each child to its respective parent.
  4. Identifying Roots: Finding nodes without parents to serve as entry points to the tree.
  5. Traversing the Tree: Optionally implementing traversal methods for processing or visualization.

By following these steps, you can efficiently transform flat relational data into meaningful hierarchical structures, facilitating better data management and analysis.

Learn More with DesignGurus.io

To deepen your understanding of data structures, algorithms, and hierarchical data management, explore these courses:

Additionally, check out the System Design Primer: The Ultimate Guide for comprehensive insights into organizing and structuring data efficiently.

Happy coding!

TAGS
Coding Interview
CONTRIBUTOR
Design Gurus Team

GET YOUR FREE

Coding Questions Catalog

Design Gurus Newsletter - Latest from our Blog
Boost your coding skills with our essential coding questions catalog.
Take a step towards a better tech career now!
Explore Answers
How do you handle a challenge?
Does Tesla send rejection emails after an interview?
Deconstructing large problems into testable sub-components
Related Courses
Image
Grokking the Coding Interview: Patterns for Coding Questions
Grokking the Coding Interview Patterns in Java, Python, JS, C++, C#, and Go. The most comprehensive course with 476 Lessons.
Image
Grokking Data Structures & Algorithms for Coding Interviews
Unlock Coding Interview Success: Dive Deep into Data Structures and Algorithms.
Image
Grokking Advanced Coding Patterns for Interviews
Master advanced coding patterns for interviews: Unlock the key to acing MAANG-level coding questions.
Image
One-Stop Portal For Tech Interviews.
Copyright © 2024 Designgurus, Inc. All rights reserved.