Ad Hierarchy Event Tracker: Worst-K Ads by CTR Under a Campaign or Ad Group

Read the full interview experience this question came from →

Quick Overview

Implement an in-memory tracker over a campaign, ad group and ad hierarchy that registers nodes, records click and impression events per date, and returns the k worst-performing ads by click-through rate under a campaign or ad group. It tests data modeling, tie and zero-impression rules, and query-time versus write-time trade-offs.

Ad Hierarchy Event Tracker: Worst-K Ads by CTR Under a Campaign or Ad Group

Company: LinkedIn

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: hard

Interview Round: Technical Screen

Implement an in-memory tracker for advertising performance over a hierarchy of ad entities. A campaign can contain ad groups and also ads directly, an ad group contains ads, and click and impression events arrive for individual ads, each tagged with a date. One possible hierarchy: ```text Campaign / \ AdGroup1 Ad1 / \ Ad2 Ad3 ``` Implement these three operations (signatures as reported, in Java): ```java // Register an ad node. It can be a campaign, an ad group, or an ad. void AddAdEntity(String nodeId, String parentId); // Consume an ad event. EventType is an enum: CLICK or IMPRESSION. void ConsumeEvent(String id, String date, EventType eventType); // Return the k worst-performing ads for the given date under the given node, // judged by ad stats such as impressions, clicks and click-through rate (CTR). // The node can be a campaign or an ad group. List<String> getWorstK(String nodeId, String date, int k); ``` Performance may be ranked by CTR, an ad's clicks divided by its impressions on that date, with a lower CTR meaning worse performance. `getWorstK` considers every ad anywhere below the node, not only its direct children: for the campaign above, that is Ad1, Ad2 and Ad3. ### Clarifying Questions - Is a campaign registered with a null parent, and can there be several campaigns? - Is every node registered before its children and before any event for it, and can a node ever move to a different parent? - Does `ConsumeEvent` always receive an ad id, or can events arrive for an ad group or a campaign? - How should an ad with no impressions on the date be treated, since its CTR is undefined, and should ads with no events that day appear at all? - How are ties in CTR broken, and what is returned when fewer than k ads exist under the node? - What format do dates have, and could a query ever cover a range of dates instead of one day? - Which call dominates in volume, events or queries? ### Part 1 — Register entities and record events Implement `AddAdEntity` and `ConsumeEvent` so that any node can be found by its id, each ad's impressions and clicks are kept per date, and the ads below any campaign or ad group can be reached. ```hint Follow one event Every call names a node by id. Think about what you need to find that node quickly, and which other nodes' answers can change when one ad receives an event. ``` #### What This Part Should Cover - A node representation and an id-to-node index built during registration - Per-date impression and click counters for each ad - Handling of unknown ids, duplicate registrations and events sent to a node that is not an ad ### Part 2 — Return the worst K ads Implement `getWorstK` for a campaign or an ad group: rank every ad below the node by its performance on the given date and return up to k ad ids, worst first. ```hint Pin the order first Before writing the query, decide exactly how two ads compare, including equal CTRs and an ad with zero impressions, so that the result is unique. ``` #### What This Part Should Cover - Correct scope: every descendant ad at any depth, such as Ad1 directly under the campaign as well as Ad2 and Ad3 under the ad group - A total ordering with explicit rules for ties and for ads without impressions, compared without floating-point surprises - Selecting the k worst without sorting the whole subtree when k is small ### Part 3 — Keep queries fast on large campaigns Suppose `getWorstK` is called often on campaigns with many ads. Discuss how to avoid walking and ranking the whole subtree on every query, and what that costs each time an event arrives. ```hint Where the work happens Compare paying the ranking cost when an event arrives with paying it when a query arrives. If you chose the first, which nodes would have to change on each event? ``` #### What This Part Should Cover - Query-time computation versus keeping a per-node, per-date ordering that is updated on write - The cost of moving one ad within that ordering when its CTR changes, across all of its ancestors - Memory growth across many dates, and how old dates are expired ### What a Strong Answer Covers - Clarifies the ranking key, the tie rule and the treatment of zero-impression ads before coding - Chooses a data model that matches an explicit parent-child hierarchy addressed by id, and justifies it - Correct, compilable code for all three operations - Time and space complexity per operation for both the query-time and the write-time approaches, with a reasoned choice between them - Edge cases: unknown ids, `k <= 0`, k larger than the number of ads, nodes without ads, and dates without events ### Follow-up Questions - Advertisers want the worst K over a range of dates rather than one day. What changes? - Events arrive from many threads at once. How do you make the tracker thread-safe without one global lock? - An ad can be moved from one ad group to another. What has to be updated? - A click and its impression can arrive out of order, or the same event can arrive twice. How do you keep CTR meaningful?

Overview: Implement an in-memory tracker over a campaign, ad group and ad hierarchy that registers nodes, records click and impression events per date, and returns the k worst-performing ads by click-through rate under a campaign or ad group. It tests data modeling, tie and zero-impression rules, and query-time versus write-time trade-offs.

Read the full LinkedIn Software Engineer interview experience this question came from

LinkedIn logo
LinkedIn
Oct 30, 2026
hardSoftware EngineerTechnical ScreenSoftware Engineering Fundamentals
0
0

Implement an in-memory tracker for advertising performance over a hierarchy of ad entities. A campaign can contain ad groups and also ads directly, an ad group contains ads, and click and impression events arrive for individual ads, each tagged with a date. One possible hierarchy:

              Campaign
             /        \
        AdGroup1      Ad1
        /      \
      Ad2      Ad3

Implement these three operations (signatures as reported, in Java):

// Register an ad node. It can be a campaign, an ad group, or an ad.
void AddAdEntity(String nodeId, String parentId);

// Consume an ad event. EventType is an enum: CLICK or IMPRESSION.
void ConsumeEvent(String id, String date, EventType eventType);

// Return the k worst-performing ads for the given date under the given node,
// judged by ad stats such as impressions, clicks and click-through rate (CTR).
// The node can be a campaign or an ad group.
List<String> getWorstK(String nodeId, String date, int k);

Performance may be ranked by CTR, an ad's clicks divided by its impressions on that date, with a lower CTR meaning worse performance. getWorstK considers every ad anywhere below the node, not only its direct children: for the campaign above, that is Ad1, Ad2 and Ad3.

Clarifying Questions Guidance

  • Is a campaign registered with a null parent, and can there be several campaigns?
  • Is every node registered before its children and before any event for it, and can a node ever move to a different parent?
  • Does ConsumeEvent always receive an ad id, or can events arrive for an ad group or a campaign?
  • How should an ad with no impressions on the date be treated, since its CTR is undefined, and should ads with no events that day appear at all?
  • How are ties in CTR broken, and what is returned when fewer than k ads exist under the node?
  • What format do dates have, and could a query ever cover a range of dates instead of one day?
  • Which call dominates in volume, events or queries?

Part 1 — Register entities and record events

Implement AddAdEntity and ConsumeEvent so that any node can be found by its id, each ad's impressions and clicks are kept per date, and the ads below any campaign or ad group can be reached.

What This Part Should Cover Guidance

  • A node representation and an id-to-node index built during registration
  • Per-date impression and click counters for each ad
  • Handling of unknown ids, duplicate registrations and events sent to a node that is not an ad

Part 2 — Return the worst K ads

Implement getWorstK for a campaign or an ad group: rank every ad below the node by its performance on the given date and return up to k ad ids, worst first.

What This Part Should Cover Guidance

  • Correct scope: every descendant ad at any depth, such as Ad1 directly under the campaign as well as Ad2 and Ad3 under the ad group
  • A total ordering with explicit rules for ties and for ads without impressions, compared without floating-point surprises
  • Selecting the k worst without sorting the whole subtree when k is small

Part 3 — Keep queries fast on large campaigns

Suppose getWorstK is called often on campaigns with many ads. Discuss how to avoid walking and ranking the whole subtree on every query, and what that costs each time an event arrives.

What This Part Should Cover Guidance

  • Query-time computation versus keeping a per-node, per-date ordering that is updated on write
  • The cost of moving one ad within that ordering when its CTR changes, across all of its ancestors
  • Memory growth across many dates, and how old dates are expired

What a Strong Answer Covers Guidance

  • Clarifies the ranking key, the tie rule and the treatment of zero-impression ads before coding
  • Chooses a data model that matches an explicit parent-child hierarchy addressed by id, and justifies it
  • Correct, compilable code for all three operations
  • Time and space complexity per operation for both the query-time and the write-time approaches, with a reasoned choice between them
  • Edge cases: unknown ids, k <= 0 , k larger than the number of ads, nodes without ads, and dates without events

Follow-up Questions Guidance

  • Advertisers want the worst K over a range of dates rather than one day. What changes?
  • Events arrive from many threads at once. How do you make the tracker thread-safe without one global lock?
  • An ad can be moved from one ad group to another. What has to be updated?
  • A click and its impression can arrive out of order, or the same event can arrive twice. How do you keep CTR meaningful?