Everything Beneath
A medium Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- medium
- Seniority
- mid
- Asked in
- Veeva Systems
Problem
A life-sciences CRM stores customer accounts in a hierarchy: each record names its account id, the parent account it sits under (None for a top-level account), and the sales booked directly to it. For every account, report the combined sales of that account together with all the accounts below it, anywhere down the chain. Accounts with no children contribute only their own sales, and the records may describe several independent top-level accounts.
Every account carries the weight of all it contains.
Example 1
Input:
accounts = [{"id":"north_health","sales":100,"parent":null},{"id":"mercy_hospital","sales":50,"parent":"north_health"},{"id":"cardiology","sales":30,"parent":"mercy_hospital"},{"id":"oncology","sales":20,"parent":"mercy_hospital"},{"id":"west_clinic","sales":40,"parent":"north_health"}]Output:
{"oncology":20,"cardiology":30,"west_clinic":40,"north_health":240,"mercy_hospital":100}Example 2
Input:
accounts = [{"id":"east_region","sales":10,"parent":null},{"id":"west_region","sales":5,"parent":null},{"id":"downtown","sales":7,"parent":"east_region"}]Output:
{"downtown":7,"east_region":17,"west_region":5}Worked solution and explanation
Why this problem exists in real interviews
This is a subtree sum hiding inside a CRM account hierarchy. The accounts arrive as a flat list of parent pointers, not as a tree, so the real skill being probed is whether you can reconstruct the hierarchy from parent references and then total each account with everything beneath it in ONE pass. The trap is the obvious O(n^2) reading: for each account, walk all of its descendants again. On a deep org with thousands of accounts that re-walks the same branches over and over. The clean answer builds a children index once and lets each node's total reuse its children's totals.
Break down the requirements
Step 1: Invert the parent pointers into a children index
The input gives you child to parent, but a rollup needs parent to children. Walk the list once, recording each account's own sales and appending each account id to its parent's child list. Seed an empty child list for EVERY id so leaf nodes have an entry too, which removes a KeyError special case later.
Step 2: Find the roots, not just the first record
A top-level account is any record whose parent is None. There can be more than one, because the data may describe several independent health systems. Start a traversal from each root; assuming a single root is the most common way this breaks on the hidden forest case.
Step 3: Total each subtree once and remember it
A node's total is its own sales plus the totals of its children. Compute it depth first and store it as you unwind, so each account is visited exactly once. The recursion naturally handles arbitrary depth and the leaf case (no children means the total is just the node's own sales).
The solution
def rollup_sales(accounts):
children = {}
own_sales = {}
for acct in accounts:
acct_id = acct["id"]
own_sales[acct_id] = acct["sales"]
children.setdefault(acct_id, [])
parent = acct["parent"]
if parent is not None:
children.setdefault(parent, []).append(acct_id)
totals = {}
def subtree_total(node):
total = own_sales[node]
for child in children[node]:
total += subtree_total(child)
totals[node] = total
return total
for acct in accounts:
if acct["parent"] is None:
subtree_total(acct["id"])
return totals- Naive per-account walk
- For each account, recursively descend and sum its descendants from scratch. Correct, but a node near the root is re-summed once for every ancestor, giving O(n * depth) and quadratic behavior on a tall hierarchy.
- Index once, total once
- Build the children index once, then each subtree total is computed a single time and reused by its parent. Every account is touched once: O(n).
Common follow-up questions
- What if a parent id can reference an account that is not present in the input? (Tests defensive handling: treat dangling parents as roots, or validate and raise. Forces a decision about data quality rather than crashing on a missing key.)
- How would you detect and reject a cycle, where an account is its own ancestor? (Tests a visited or in-progress set during traversal, since the recursion would otherwise overflow the stack on a cyclic parent chain.)
- The hierarchy no longer fits in memory on one machine. How would you roll up sales across a partitioned account graph? (Tests level-by-level aggregation or a Pregel-style message passing toward roots, the distributed analog of the single-machine depth-first sum.)