PracHub
QuestionsPremiumCoachesLearningGuidesInterview Prep

Quick Overview

This question evaluates a candidate's ability to design and implement efficient data structures for constant-time operations and to reason about graph connectivity and component counting, falling under the Coding & Algorithms domain and requiring practical implementation with algorithmic complexity reasoning.

  • medium
  • Amazon
  • Coding & Algorithms
  • Software Engineer

Implement Cache and Count Components

Company: Amazon

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

You are asked to solve two independent coding problems in this session, each followed by a short discussion. This mirrors a 60-minute Amazon SDE coding round: implement a correct, efficient solution, then walk through complexity and edge cases. ### Constraints & Assumptions - **Problem 1 (LRU Cache):** `1 <= capacity`. Keys and values are integers. `get` and `put` are each called many times (assume up to ~$10^5$ operations), so both must run in $O(1)$ average time — a per-operation linear scan will not pass. - **Problem 2 (Connected Groups):** `1 <= n`; nodes are labeled `0` to `n - 1`. `edges[i] = [a, b]` is undirected, `0 <= a, b < n`. The graph may contain self-loops, duplicate edges, and isolated nodes. Assume up to ~$10^5$ nodes and edges. - Single-threaded; you do not need to make either structure thread-safe unless the interviewer asks. ### Part 1: Implement an LRU Cache Design a fixed-capacity **least-recently-used** cache supporting these operations, each in $O(1)$ average time: - `LRUCache(int capacity)`: initialize the cache with a positive capacity. - `int get(int key)`: return the value for `key` if present, else `-1`. A successful `get` marks `key` as the **most recently used** item. - `void put(int key, int value)`: insert or update `key`. If inserting a new key would exceed `capacity`, **evict the least recently used** key first. A `put` also marks `key` as most recently used. ```hint Two structures together No single built-in structure gives you both $O(1)$ keyed lookup *and* $O(1)$ recency reordering. Combine one structure for lookup with another for ordering. ``` ```hint Ordering structure For the recency side, you need a structure that lets you detach an arbitrary element and re-insert it at one end in $O(1)$ — *provided* you already hold a reference to it. Think about which linked structure supports $O(1)$ removal from the middle, and how the lookup structure could hand you that reference directly. ``` ```hint Edge cases to handle Updating an existing key (move, don't duplicate); evicting only when a *new* key overflows capacity; using sentinel head/tail dummy nodes to avoid null checks on every splice. ``` ### Part 2: Count Connected Groups in an Undirected Graph Given an integer `n` (nodes labeled `0` to `n - 1`) and a list of undirected `edges`, return the number of connected components. Isolated nodes count as their own group. ```text Input: n = 5, edges = [[0,1], [1,2], [3,4]] Output: 2 Explanation: Nodes {0,1,2} form one group, and nodes {3,4} form another. ``` ```hint How do you count "reachable groups"? There are two classic families here: one explores the graph directly and counts how many separate explorations it takes to cover every node; the other incrementally merges nodes as you process edges and asks how many independent clusters remain. Decide which fits before committing to data structures. ``` ```hint Why isolated nodes matter A node with no edges is still a component. Initialize as if every node is its own group, then merge — don't only iterate over endpoints that appear in `edges`. ``` ### Clarifying Questions to Ask - For the LRU cache, can `capacity` be `0`, or is it guaranteed positive? Are keys/values always non-negative? - On a `put` that updates an existing key, should it count as a "use" that refreshes recency? (Standard LeetCode semantics: yes.) - For the graph, can `edges` contain self-loops (`[a, a]`) or duplicate edges? Should they be tolerated? - Are node labels guaranteed dense (`0..n-1`), or could they be sparse / non-integer? - What are the expected input sizes — does this need to be $O(1)$ / near-linear, or is a simpler approach acceptable? ### What a Strong Answer Covers - **LRU:** correct $O(1)$ design (hash map + doubly linked list, or an ordered-dict equivalent) with the move-to-front, update-in-place, and eviction logic all correct. - Clean handling of the update-existing-key case without leaking a duplicate node, and eviction triggered only on genuine overflow. - **Graph:** a correct linear-time component count (DFS/BFS or Union-Find) that accounts for isolated nodes, self-loops, and duplicate edges. - Accurate time and space complexity for both ($O(1)$ per cache op; $O(n + m)$ for the graph), stated explicitly. - Communication: stating assumptions, dry-running a small example, and naming trade-offs (e.g. DFS recursion depth vs. iterative/Union-Find). ### Follow-up Questions - How would you make the LRU cache thread-safe, and where would lock contention hurt? Could you shard it? - How would you extend the cache to an **LFU** (least-frequently-used) eviction policy while keeping operations $O(1)$? - For the graph, how would you also return the **size of the largest** connected component, or the sizes of all components? - If the graph edges arrive as a stream (online), which approach scales better, and what does Union-Find's near-constant amortized cost depend on?

Quick Answer: This question evaluates a candidate's ability to design and implement efficient data structures for constant-time operations and to reason about graph connectivity and component counting, falling under the Coding & Algorithms domain and requiring practical implementation with algorithmic complexity reasoning.

Part 1: Implement an LRU Cache

Design a fixed-capacity least-recently-used cache. The cache stores integer keys and integer values. A `get` operation returns the value for a key if present, otherwise `-1`, and makes that key the most recently used. A `put` operation inserts or updates a key-value pair and makes that key the most recently used. If inserting causes the cache to exceed capacity, evict the least recently used key. Implement this through the function `solution(capacity, operations)`, where operations encode cache calls.

Constraints

  • 1 <= capacity <= 100000
  • 0 <= len(operations) <= 200000
  • Each operation is either `[0, key]` or `[1, key, value]`
  • Keys and values fit in signed 32-bit integers
  • Both `get` and `put` should run in O(1) average time

Examples

Input: (2, [[1, 1, 1], [1, 2, 2], [0, 1], [1, 3, 3], [0, 2], [1, 4, 4], [0, 1], [0, 3], [0, 4]])

Expected Output: [1, -1, -1, 3, 4]

Explanation: Key 2 is evicted when key 3 is inserted, and key 1 is evicted when key 4 is inserted.

Input: (2, [[1, 1, 1], [1, 2, 2], [1, 1, 10], [1, 3, 3], [0, 2], [0, 1], [0, 3]])

Expected Output: [-1, 10, 3]

Explanation: Updating key 1 makes it most recently used, so key 2 is evicted when key 3 is inserted.

Input: (1, [[1, 5, 50], [0, 5], [1, 6, 60], [0, 5], [0, 6], [1, 6, 61], [0, 6]])

Expected Output: [50, -1, 60, 61]

Explanation: With capacity 1, every new key evicts the previous key.

Input: (3, [])

Expected Output: []

Explanation: There are no operations, so there are no get results.

Hints

  1. Use a hash map to find a cache entry by key in O(1) average time.
  2. Use a doubly linked list to maintain least-recently-used to most-recently-used order, moving nodes whenever they are accessed.

Part 2: Count Connected Groups in an Undirected Graph

You are given `n` nodes labeled from `0` to `n - 1` and a list of undirected edges. Return the number of connected groups, also called connected components, in the graph. Nodes with no edges count as their own connected groups.

Constraints

  • 0 <= n <= 100000
  • 0 <= len(edges) <= 200000
  • Each edge has exactly two integers `[a, b]`
  • For every edge, 0 <= a < n and 0 <= b < n
  • Duplicate edges and self-loops may appear and should not change the answer incorrectly

Examples

Input: (5, [[0, 1], [1, 2], [3, 4]])

Expected Output: 2

Explanation: Nodes {0, 1, 2} form one group and nodes {3, 4} form another.

Input: (4, [])

Expected Output: 4

Explanation: With no edges, every node is isolated and forms its own group.

Input: (1, [])

Expected Output: 1

Explanation: A single node with no edges is one connected group.

Input: (0, [])

Expected Output: 0

Explanation: An empty graph has no connected groups.

Input: (3, [[0, 0], [0, 1], [1, 0]])

Expected Output: 2

Explanation: The self-loop on 0 and duplicate edge between 0 and 1 do not create extra groups; {0, 1} and {2} are the two groups.

Hints

  1. You can traverse the graph with DFS or BFS and count how many times you start from an unvisited node.
  2. Alternatively, use Union-Find: start with `n` groups and merge the groups for each edge.
Last updated: Jun 17, 2026

Loading coding console...

PracHub

Master your tech interviews with 8,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Implement Top-p (Nucleus) Sampling in NumPy - Amazon (medium)
  • Implement Multi-Head Attention from Scratch in NumPy - Amazon (medium)
  • Detect and Break a Cycle in a Singly Linked List - Amazon (medium)
  • Caesar Cipher with Translation-Table Optimization - Amazon (medium)
  • Minimum Drone Delivery Time on a Ring of Hubs - Amazon (medium)