PracHub
QuestionsCoachesLearningGuidesInterview Prep
|Home/Software Engineering Fundamentals/Apple

Design a deck of cards with shuffle/draw

Last updated: Jun 24, 2026

Quick Overview

This question evaluates object-oriented design skills, understanding of randomness and probability in algorithms, and analysis of time/space complexity for data structures and operations.

  • medium
  • Apple
  • Software Engineering Fundamentals
  • Software Engineer

Design a deck of cards with shuffle/draw

Company: Apple

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Technical Screen

## Object-Oriented Design + Randomness: A Deck of Cards Design an in-memory model of a standard 52-card playing deck. Your design will be exercised by client code that builds a fresh deck, shuffles it, and draws cards one at a time until the deck is exhausted. Create classes to represent at least: - A **card** holding a rank (Ace through King) and a suit (Clubs, Diamonds, Hearts, Spades). - A **deck** that owns a collection of cards. The deck must support two operations: 1. `shuffle()` — randomizes the order of the cards currently remaining in the deck. 2. `draw()` — removes one card from the deck and returns it. Your design must also satisfy two correctness/probability requirements: - After `shuffle()`, every ordering (permutation) of the remaining cards is equally likely — i.e., the shuffle is **unbiased**. - Each `draw()` returns each currently-remaining card with **equal probability** at that moment. Walk through your class design, justify your data-structure choice, give the algorithm for `shuffle()` and `draw()`, argue why the probability requirements hold, and state the time/space complexity of each operation. Sketch the core methods in your language of choice. ```hint Where to start Separate the *value* (an immutable card) from the *container* (a mutable deck). Build the full 52-card deck by taking the cross product of the 4 suits and 13 ranks. Pick the deck's internal representation by asking which operation has to be fast: removing a card. ``` ```hint Data structure A dynamic array / list backs both operations cheaply if you treat one *end* as the "top." Removing from the end (pop) is $O(1)$ amortized; removing from the front forces an $O(n)$ shift. Avoid a structure (like a singly linked list) that makes random index access $O(n)$ — the shuffle needs random indexing. ``` ```hint Unbiased shuffle The standard correct algorithm is the **Fisher–Yates (Knuth) shuffle**: iterate $i$ from $n-1$ down to $1$, pick $j$ uniformly in $[0, i]$, and swap positions $i$ and $j$. Beware the two classic traps — picking $j$ from the full range $[0, n-1]$ on every step, or "sort by a random key" — both produce biased orderings. ``` ```hint Uniform draw without relying on shuffle If you want `draw()` to be uniform on its own (not depending on a prior shuffle), pick a random index $k \in [0, n-1]$, swap that card with the last card, then pop the last. This keeps `draw()` at $O(1)$ and never invalidates the deck. ``` ### Constraints & Assumptions - A standard deck has exactly 52 distinct cards (4 suits × 13 ranks); no jokers unless you choose to support them. - The deck holds no duplicates; a drawn card is gone until the deck is rebuilt/reset. - A good pseudo-random number generator is available (e.g., a `Random`-style API that yields a uniform integer in a half-open range). - The deck is small and fully in memory; persistence, networking, and multi-deck shoes are out of scope unless you raise them. ### Clarifying Questions to Ask - Should `shuffle()` shuffle only the *remaining* cards, or always reset to a full 52 and then shuffle? - What is the desired behavior of `draw()` on an empty deck — exception, sentinel/`null`, or an `Optional`-style empty value? - Does the deck need to be thread-safe (drawn from multiple threads concurrently), or is single-threaded use acceptable? - Should the PRNG be injectable (for deterministic, seedable tests), or is a built-in default acceptable? - Are duplicate cards or multi-deck "shoes" (e.g., blackjack's 6-deck shoe) ever required? - Do we need card equality/ordering (e.g., for comparing hands), or only identity and printing? ### What a Strong Answer Covers - **Clean class decomposition:** an immutable card value object (rank + suit, with sensible equality and a readable string form) and a deck that owns the card collection plus its randomness source. - **Justified data structure:** a list/array with one end as the "top," explaining why pop-from-end gives $O(1)$ draw and supports in-place shuffling, and why other choices are worse. - **Correct unbiased shuffle:** Fisher–Yates stated precisely, with a probability argument that all $n!$ orderings are equally likely, and explicit mention of the biased anti-patterns to avoid. - **Correct uniform draw:** either "draw the top after an unbiased shuffle" with a uniformity argument, or the random-index swap-and-pop technique that is uniform without a prior shuffle. - **Complexity targets:** $O(n)$ time / $O(1)$ extra space for `shuffle()`; $O(1)$ time and space for `draw()`. - **Edge cases & API design:** explicit, documented behavior for an empty deck; testability via an injectable PRNG; awareness of thread-safety and secure-randomness trade-offs raised at the right altitude. ### Follow-up Questions - How would you unit-test that `shuffle()` is unbiased and `draw()` is uniform, given that the output is random? (Hint: seed the PRNG for determinism; use a chi-square / frequency test over many trials for the distribution.) - Extend the design to a multi-deck "shoe" (e.g., 6 decks) and a "burn/discard" pile, then a `reshuffle()` that folds the discards back in. What changes? - If two players draw concurrently from the same deck, how do you guarantee no card is dealt twice and the draw stays uniform? Compare a coarse lock vs. a single-threaded dealer. - Suppose you must support an "infinite" stream of uniformly random cards with replacement (a card can repeat). How does the data structure and `draw()` change?

Quick Answer: This question evaluates object-oriented design skills, understanding of randomness and probability in algorithms, and analysis of time/space complexity for data structures and operations.

Related Interview Questions

  • Design a Population-Weighted Random Country Picker - Apple (medium)
  • Walk Through an Agentic PDF Evaluation Codebase - Apple (hard)
  • How to root-cause Wi‑Fi chip stops after 30 minutes - Apple (medium)
  • How to debug a Python loop-condition bug - Apple (medium)
  • Explain thermal and signal fundamentals - Apple (hard)
|Home/Software Engineering Fundamentals/Apple

Design a deck of cards with shuffle/draw

Apple logo
Apple
Mar 1, 2026, 12:00 AM
mediumSoftware EngineerTechnical ScreenSoftware Engineering Fundamentals
39
0

Object-Oriented Design + Randomness: A Deck of Cards

Design an in-memory model of a standard 52-card playing deck. Your design will be exercised by client code that builds a fresh deck, shuffles it, and draws cards one at a time until the deck is exhausted.

Create classes to represent at least:

  • A card holding a rank (Ace through King) and a suit (Clubs, Diamonds, Hearts, Spades).
  • A deck that owns a collection of cards.

The deck must support two operations:

  1. shuffle() — randomizes the order of the cards currently remaining in the deck.
  2. draw() — removes one card from the deck and returns it.

Your design must also satisfy two correctness/probability requirements:

  • After shuffle() , every ordering (permutation) of the remaining cards is equally likely — i.e., the shuffle is unbiased .
  • Each draw() returns each currently-remaining card with equal probability at that moment.

Walk through your class design, justify your data-structure choice, give the algorithm for shuffle() and draw(), argue why the probability requirements hold, and state the time/space complexity of each operation. Sketch the core methods in your language of choice.

Constraints & Assumptions

  • A standard deck has exactly 52 distinct cards (4 suits × 13 ranks); no jokers unless you choose to support them.
  • The deck holds no duplicates; a drawn card is gone until the deck is rebuilt/reset.
  • A good pseudo-random number generator is available (e.g., a Random -style API that yields a uniform integer in a half-open range).
  • The deck is small and fully in memory; persistence, networking, and multi-deck shoes are out of scope unless you raise them.

Clarifying Questions to Ask

  • Should shuffle() shuffle only the remaining cards, or always reset to a full 52 and then shuffle?
  • What is the desired behavior of draw() on an empty deck — exception, sentinel/ null , or an Optional -style empty value?
  • Does the deck need to be thread-safe (drawn from multiple threads concurrently), or is single-threaded use acceptable?
  • Should the PRNG be injectable (for deterministic, seedable tests), or is a built-in default acceptable?
  • Are duplicate cards or multi-deck "shoes" (e.g., blackjack's 6-deck shoe) ever required?
  • Do we need card equality/ordering (e.g., for comparing hands), or only identity and printing?

What a Strong Answer Covers

  • Clean class decomposition: an immutable card value object (rank + suit, with sensible equality and a readable string form) and a deck that owns the card collection plus its randomness source.
  • Justified data structure: a list/array with one end as the "top," explaining why pop-from-end gives O(1)O(1)O(1) draw and supports in-place shuffling, and why other choices are worse.
  • Correct unbiased shuffle: Fisher–Yates stated precisely, with a probability argument that all n!n!n! orderings are equally likely, and explicit mention of the biased anti-patterns to avoid.
  • Correct uniform draw: either "draw the top after an unbiased shuffle" with a uniformity argument, or the random-index swap-and-pop technique that is uniform without a prior shuffle.
  • Complexity targets: O(n)O(n)O(n) time / O(1)O(1)O(1) extra space for shuffle() ; O(1)O(1)O(1) time and space for draw() .
  • Edge cases & API design: explicit, documented behavior for an empty deck; testability via an injectable PRNG; awareness of thread-safety and secure-randomness trade-offs raised at the right altitude.

Follow-up Questions

  • How would you unit-test that shuffle() is unbiased and draw() is uniform, given that the output is random? (Hint: seed the PRNG for determinism; use a chi-square / frequency test over many trials for the distribution.)
  • Extend the design to a multi-deck "shoe" (e.g., 6 decks) and a "burn/discard" pile, then a reshuffle() that folds the discards back in. What changes?
  • If two players draw concurrently from the same deck, how do you guarantee no card is dealt twice and the draw stays uniform? Compare a coarse lock vs. a single-threaded dealer.
  • Suppose you must support an "infinite" stream of uniformly random cards with replacement (a card can repeat). How does the data structure and draw() change?
Loading comments...

Browse More Questions

More Software Engineering Fundamentals•More Apple•More Software Engineer•Apple Software Engineer•Apple Software Engineering Fundamentals•Software Engineer Software Engineering Fundamentals

Write your answer

Your first approved answer each day earns 20 XP.

Sign in to write your answer.
PracHub

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

Product

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

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding 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.