Chapter 67 Apple

67.1 Compound Iterator

Suitable for: Everyone
Time: 10-20 minutes
Format: Phone-Screen
Skills: Coding in Java, Data structures, Java Collections, using an interface

To initialize a CoderPad for this problem, go to and select iTunes: Compound Iterator and press the Create Pad With Question button.

67.1.1 The Problem

Write a CompoundIterator. CompoundIterator takes an array of Iterators in its constructor; CompoundIterator itself should implement the java.util.Iterator interface, such that it iterates over the elements of the passed-in Iterators as if they were a single Iterator. If any of the Iterators in the passed-in array is null or empty, it should simply be skipped (note that this is not the same thing as saying that if the Iterators themselves return null elements from next(), those elements should be skipped — they should not be). It is expected that CompoundIterator will exhaust the passed-in Iterators. You do not need to provide a working implementation of the remove() method.

Use the following stub to start:

67.1.2 For the Interviewer

For more experienced candidates, add this stipulation: Some of the passed-in Iterators may be iterating over data structures that, either individually or in aggregate, are too large to hold in memory at one time (e.g., large result sets from a database), and so it is not acceptable to collapse the elements of the Iterators into a single in-memory data structure.

Encourage the candidate to think aloud about data structure and design choices.

67.1.3 Hints

  • How far do you have to look ahead to tell whether there is a next element?

67.1.4 Followup Questions

For less experienced candidates who choose to collapse the Iterators into a single data structure — which is arguably the simplest (and therefore best) solution in the absence of a requirement not to collapse them — you might ask, “Under what circumstances would it be inappropriate to collapse the Iterators into a single data structure?”

67.1.5 Solution

67.1.6 Unit Test

67.1.7 Evaluation

Run the Unit Test and evaluate the candidate’s code.

67.2 Implement Natural Numbers

Suitable for: Mid-Senior Level.
Time: 15-25 minutes
Format: In-Person
Skills: Abstract thinking, OOP, Java, asking clarifying questions and coding to spec.

67.2.1 The Problem

In a nutshell the problem is to implement Natural (non-negative) numbers without using int or any other numbers in java.

Basically a candidate is given a subset of Java language that has:

  • OOP concepts:

    • classes
    • interfaces
    • methods
  • Basic language structures:

    • local variables
    • booleans
    • flow control statements (if/elseif/else)
    • while loops

We ask a candidate to define non-negative numbers: [0, +infinity] using a given interface:

n.previous()就是减一(n-1),就是加一(n+1)的意思. Questions to ask

  • Ask candidate to implement this interface according to specification. Note that it is important to understand that there is no fields allowed
  • Ask candidate to implement following methods, ask about time complexity: Optional
  • Talk about infinity, how can we implement a number that represents infinity, how can we add a method that detects infinity? How do we need to change operations to support that.
  • Talk about rational numbers (or complex numbers)

67.3 Implement Natural Numbers 2

Suitable for: Mid Level.
Time: 30 minutes
Format: In-Person or Phone-Screen
Skills: Abstract thinking, Brain teaser, Algorithms

67.4 N-Way-List-Merge

Suitable for: Everyone
Time: 15-35 minutes
Format: Phone-Screen
Skills: Algorithms, Data structures, Java Collections

To initialize a CoderPad for this problem, go to and select AMP: N-Way-List-Merge and press the Create Pad With Question button.

67.4.1 The Problem

  • You are given a task to implement a function that merges several sorted arrays (lists).
  • Candidate should be able to come up with an O(N * Log M) solution. Where N is a total number of elements and M is a number of lists given. There are two ways of doing this:
    • Using MinHeap (PriorityQueue) and extract one element at a time
    • Merge pairs of lists, then merged pairs and so on (more complicated way of doing that code-wise)

67.4.2 Solutions

These are the real interview answers that we’ve accepted and invited people on-site. By-pair Merging

import java.util.*;

class Solution {
     * Accepts an array of sorted lists and merges them into one sorted list
     * Example: [1, 3, 5, 6],[3, 4, 7],[-1, 2]=>[-1, 1, 2, 3, 3, 4, 5, 6, 7]
     * [[], [], []] -> []
     * [] -> []

    // M - # lists
    // N - # elements
    // O(...) = O(N*log(M))
    public static List<Integer> merge(List<List<Integer>> lists) {
        //l1, l2, l3, l4, l5, l6, l7, l8
        // 1) l1 & l2, l3 & l4, l5 & l6, l7 & l8 -> 4 lists l12,l34,l56,l78
        // 2) l12 & l34, l56 & l78 -> l1234, l5678
        // 3) l1234 & l5678 -> result
        // throw new UnsupportedOperationException("Please implement this method");

        // Base condition check
        if (lists.size() == 0) {
            return new ArrayList<Integer>();
        } else if (lists.size() == 1) {
            return lists.get(0);

        // Clone the input
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        for (List<Integer> l : lists) {

        int numLists = lists.size();
        int numProcessed = 0;
        int start;
        while (numLists > 1) {
            start = 0;
            if (numLists % 2 != 0) {
                start = 1;

            // Process all current lists
            for (int i = start; i < numLists; i += 2) {
                res.set(numProcessed, merge(res.get(i), res.get(i + 1)));

            numLists = numProcessed;
            numProcessed = 0;

        return res.get(0);

    private static List<Integer> merge(List<Integer> l1, List<Integer> l2) {
        int i = 0; // for l1
        int j = 0; // for l2
        List<Integer> result = new ArrayList<Integer>();
        while (i < l1.size() && j < l2.size()) {
            if (l1.get(i) < l2.get(j)) {
            } else if (l1.get(i) > l2.get(j)) {
            } else {
        // Figure out where are the remaining elements to process (if any)
        List<Integer> temp = new ArrayList<Integer>();
        int t = 0;
        if (i < l1.size()) {
            temp = l1;
            t = i;
        } else if (j < l2.size()) {
            temp = l2;
            t = j;

        for (int k = t; k < temp.size(); k++) {

        return result;

    public static void main(String[] args) {
        // [-1, 1, 2, 3, 3, 4, 5, 6, 7]
                        Arrays.asList(1, 3, 5, 6),
                        Arrays.asList(3, 4, 7),
                        Arrays.asList(-1, 2)

        // [-1, 1, 1, 2, 3, 3, 4, 5, 6, 7]
                        Arrays.asList(1, 3, 5, 6),
                        Arrays.asList(3, 4, 7),
                        Arrays.asList(-1, 2),

        // [-1, 1]

        // [1, 3, 5, 6]
                        Arrays.asList(1, 3, 5, 6)
  • C++

67.5 Optimal 2-way Split

Suitable for: Mid-Senior Level.
Time: 30 minutes
Format: Phone-Screen
Skills: Knowledge of algorithms, asking clarifying questions and coding to spec.

To initialize a CoderPad for this problem, go to and select iTunes: Optimal Two-Way Split and press Create Pad With Question.

67.5.1 The Problem

Divide an input array of numbers into 2 groups such that the absolute difference between an arithmetic mean in each group is maximized. Every element from the input array must belong to either one of the two groups. Discuss time and memory complexity of your solution.

67.5.2 For the Interviewer

Candidate may ask clarifying questions.

  1. Q: “What are the numbers in the input array?” A: “Positive and negative doubles.”
  2. Q: “Should I assume any particular order or distribution of the data in the input array?” A: “No.”
  3. Q: “Is approximate solution acceptable?” A: “No, only exact solution.”
  4. Q: “Should I consider illegal input?” A: “Yes, you should signal an error if illegal arguments are provided to the algorithm.” (Illegal argument is any array that does not have 2 or more elements.
  5. Q: “Should I return 2 arrays?” A: “You should use as little memory as you can.” (A candidate might not know at this point that an answer may be represented as a single scalar, i.e. a split point that discriminates inputs: all inputs less or equal than the split point belong to one group. All the other elements belong to the other group).
  6. Q: “What if there’s more then one valid output?” A: “Return the first solution.”

Candidate may propose a couple of clarifying examples.

  • e.g. [2, 3, 1] -> {1},{2,3} or {1,2},{3} are both valid splits, in both cases the difference is maximized and equal to 1.5

Candidate should be able to propose a brute-force solution to find every possible split of data and choose the one that maximizes an objective function.

67.5.3 Hints

  • Candidate should figure out that all elements in one group will be <= all the elements in the other group. Ask them to verbally prove this.

  • Once the candidate has explored the problem, ask them if they can create a solution with O(n) time and O(1) additional memory complexity.

67.5.4 Followup Questions

  • Candidate must be able to calculate time and space complexity of solution. Brute force time complexity is \(O(2^N-2)\) and memory complexity \(O(1)\). A candidate must be able to prove what is the time complexity verbally, something like:
"Each element might either be in one or the other group, so I have 2^N possible
combinations, where N is a number of elements in input array, but I don't allow
all elements in the same group, because then I can't calculate the arithmetic
mean in one of them, so that's where -2 comes from. O(1) memory complexity comes
from the fact that the brute-force solution only requires nesting loops and no

000…000 and 111…111 are not allowed, so need to -2.

67.5.6 Evaluation

Rate the candidate based on the following scale (from 1-4): 4.0

  • Candidate proposes O(N) time, O(1) space complexity solution
  • Able to explain the complexity of his/her solution.
  • Correct code (passes all tests).
  • Candidate has a clear understanding of “impractical” solutions (brute-force solution). Was able to prove this mathematically. 3.0

  • Candidate proposes O(N) time solution but used more memory than needed.
  • Able to explain the complexity of his/her solution?
  • Correct code (passes all tests).
  • Candidate has a clear understanding of “impractical” solutions (brute-force solution) but could not rigorously prove the complexity. 2.0

  • Candidate proposes worse than O(N) time complexity solution. Usually this happens when a candidate re-calculates the average over and over.
  • Candidate is able to explain the complexity of his/her solution?
  • Correct code (passes all tests).
  • Candidate has a clear understanding of “impractical” solutions (brute-force solution) but could not rigorously prove the complexity. 1.0

  • None of the above.

67.6 Palindrome Questions

Suitable for: Mid-Senior Level.
Time: 15-45 minutes
Format: Phone-Screen or In-Person
Skills: Knowledge of algorithms, asking clarifying questions and coding to spec.

The Questions for this problem are in two parts. Start at and look for the two questions called iTunes: Palindrome Function and iTunes: Palindrome Substrings. Select the Palindrome Function question and press the Create Pad With Question button, then once the candidate has finished with that question, continue with Palindrome Substrings.

67.6.1 The Problem

A palindrome is a string that is the same when read from left to right and right to left. This task is to write a function that counts how many palindromes are in a given input string.

67.6.2 For the Interviewer

Encourage the candidate to think aloud about data structure and design choices. Tell the candidate:

  1. Implement a function that takes a string and answers if it is a palindrome or not. There are two caveats:
  • String has special characters, that are ALL non-alphabetic characters. These characters we should simply ignore. For example a?a?12312?a--a== is a palindrome, since if you strip off non-alpha chars it is aaaa
  • Lowercase letters are equal to uppercase. e.g. aBbA is a palindrome.
  1. Second question is to implement a function that counts how many palindromes a given input string contains.

To simplify, we assume that string has NO special characters and all the chars are lowercased.

For example, elephant = 8 + 1 = 9, aaa = 3 + 2 + 1 = 6.

67.6.3 Hints

  • If the candidate starts down a path that requires an algorithm with some additional memory or scratch space, let them know that they can create a more efficient function that does not need additional memory.
  • If they can’t come up with an algorithm - give a hint, that every palindrome has a center.
  • If that doesn’t help - tell that if x|word|x is a palindrome, then word is a palindrome as well.

67.6.4 Followup Questions For Part 1 (Palindrome Function)

  • Ask the candidate to work out the complexity of their algorithm. For Part 2 (Palindrome Substrings)

  • Ask for a naive solution (generate all the substrings and run through isPalindrome function)
  • Ask for complexity (expected answer is O(N^3)).
  • Ask to find a O(N^2) solution.

67.6.6 Evaluation

  • The minimum for a candidate is to at least come up with an iterative solution that doesn’t require additional memory.
  • Can estimate the complexity of their isPalindrome function.
  • Can estimate the complexity of the naive approach for the Palindrome Substrings problem (Part 2).
  • Candidate comes up with a good, clean, well structured solution to both parts of the problem.
  • Candidate spent time clarifying and asking questions and discussing their approach before jumping into the implementation.

67.6.7 Reference:

Palindrome topics

67.7 Primes Sieve

Suitable for: Everyone
Time: 30 minutes
Format: Phone-Screen
Skills: Java Coding, simple algorithms, coding to spec.

To initialize a CoderPad for this problem, go to and select the iTunes: Primes Sieve question and press the Create Pad With Question button.

67.7.1 The Problem

Sieve of Eratosthenes

To find all the prime numbers less than or equal to a given integer n by Eratosthenes’s method:

  1. Create a list of consecutive integers from 2 through n: (2, 3, 4, …, n).
  2. Initially, let p equal 2, the first prime number.
  3. Starting from p, enumerate its multiples by counting to n in increments of p, and mark them in the list (these will be 2p, 3p, 4p, etc.; the p itself should not be marked).
  4. Find the first number greater than p in the list that is not marked. If there was no such number, stop. Otherwise, let p now equal this new number (which is the next prime), and repeat from step 3.
  5. When the algorithm terminates, all the numbers in the list that are not marked are prime.

67.7.2 For the Interviewer

Encourage the candidate to think aloud about data structure and design choices.

This is an exercise in following requirements more than anything and some candidates simply ignore the algorithm and write their own prime number generator.

67.7.3 Followup Questions

Ask about the running time of the candidate’s solution. The typical solution will run in O(N log(log(N))). There is an O(N) variation. See more details here: (This is Wrong!)

67.7.4 Solution

Here is a simple solution.

Another solution that uses a HashMap instead of a boolean array:

67.7.6 Evaluation

  • Did the candidate’s solution work?
  • Did they implement the algorithm as specified?
  • Could candidate speak to the complexity of the algorithm?

67.7.7 204. Count Primes From Leetcode


Count the number of prime numbers less than a non-negative number, n.

T: \(O(Sqrt(N) * LogLogN + N)\)

T: \(O(N * LogLogN)\)

  • Why i*i?

Everyone else is explaining why for a monolithic sieve (i.e. non-segmented). You need to check all prime factors up to the square root of the limit (e.g. N=49, we must check 7, but do not need to check anything higher, since any number divisible by a larger number would also be divisible by a number less than the square root of N, hence we would already have found it). I’m pretty sure this isn’t your question, which is instead about segmented sieves.

67.8 Resource Pool

Suitable for: Everyone
Time: 30 minutes
Format: In-Person (preferably with a laptop for coding and testing)
Skills: Coding in Java, data structures, basic OO design, multithreading

To initialize a CoderPad for this problem, go to and select the iTunes: Resource Pool question and press the Create Pad With Question button.

67.8.1 The Problem

Your task is to write a thread-safe ResourcePool in Java; it should have a constructor, taking an int that should be interpreted as the size of the pool. Other than the constructor, it only needs two public instance methods: claimResource() and releaseResource(). You can assume, for the purposes of this problem, that clients will always release Resources that they claim, and they will do so once, and only once.

Also, you can assume that the class Resource has a default, no-argument constructor (this is, admittedly, artificial, but it keeps things simple, and it keeps us focused on the ResourcePool). The class Resource is provided so that your code will compile. You cannot edit Resource, though; for the purposes of this problem, Resource looks like this:

Below is a skeleton of the class that you should fill in. We’ll compile the class and test it.

67.8.2 For The Interviewer

Tell the candidate the following:

You are working in a high-volume multi-threaded application, like a web application, and your threads need to work with a class, let’s call it Resource, whose instances are rather expensive to construct, and not thread-safe; i.e., a given instance of Resource should only be used by one thread at a time. Once a given thread is done with the Resource, it could be used by another thread. Lots of threads need to use Resources for short periods of time, but once they’re done with the Resource, they may not need one again for a while, if at all.

  • Encourage the candidate to think out loud about data structure choices.
  • Encourage candidate to ask questions about anything that is unclear

There is one aspect of the problem deliberately left unspecified, so that the candidate needs to ask questions to drive out the specifics:

  • Candidate asks: What happens when someone calls claimResource() and they’re all claimed?
  • Interviewer responds: “What do you think should happen when the pool is empty? What are your options?”

(For example: grow the pool, make the client wait until one is available, throw an exception, just return null.)

After discussing the options, tell the candidate that they should just return null if the pool is empty.

If there is time after the problem is solved, you can move on to a version that uses wait() and notify().

67.8.3 Hints

  • Many candidates start out thinking they need to keep track of Resources that are already claimed; most experienced candidates realize pretty quickly that the requirements of the problem are such that you only need to keep track of Resources that are currently available to be claimed.

  • If the candidate chooses to use a Hashtable or similar key-value data structure, and does not switch directions relatively soon, you might ask, “I see you’ve chosen to use a Hashtable. What will you use as the key for each entry? Why?”

  • The wait() method throws InterruptedException, which can typically be caught and ignored.

67.8.4 Followup Questions

  • Ask the candidate why they chose the data structure(s) they did. Ask them what others they might have chosen, and what advantages there might be to one or the other.
  • You might wish to discuss thread synchronization. Make the candidate walk you through how a race condition could occur, and have them point to the specific location in the code where having control pass to another thread would cause trouble. Ask the candidate whether everything they’ve synchronized needs to be synchronized, or whether the solution would still work with one of the synchronized keywords removed. (Depending on the solution, releaseResource() might or might not need explicit synchronization.)
  • You might wish to discuss the fact that the wait()/notify() solution is susceptible to starvation.
  • You might wish to discuss how a real ResourcePool would differ from this one; how it would use an initial size, a maximum size, and a policy for how large to grow the pool at once, etc.
  • You might wish to discuss what a client would have to do in order to make certain that claimed Resources are invariably released. (For example, using a finally block could help with this.)
  • You might wish to discuss the special considerations that apply to database connection pools. See if the candidate has an understanding that: (a) database connections can go stale and may need to be re-established, (b) database connections are often limited in number by policy; the database may not let you create arbitrarily many connections.
  • If there’s time, you can even discuss with the candidate how they would write a JUnit test to test their class.

67.8.6 Unit Test

Here is a JUnit test test their solution. Note that the constant EMPTY_POOL_RETURNS_NULL can be set to true for the initial solution, and then flipped to false to test the wait()/notify() solution.

import java.util.*;
import org.junit.*;
import static org.junit.Assert.*;
public class ResourcePoolTest{
  private static final boolean VERBOSE = false;
  private static final boolean EMPTY_POOL_RETURNS_NULL = true;
  private static final int POOL_SIZE = 10;
  private static final int NUM_THREADS = 20;
  private static final int NUM_ITERATIONS_PER_THREAD = 400;
  private static final long MAX_SLEEP_MS = 50L;
  private static final long THREAD_GROUP_WAIT_TIMEOUT =
  private static Random sRandom = new Random(System.currentTimeMillis());
  private Set mClaimedResources = null;
  private ResourcePool mPool = null;
  private int mMaxClaimedResources = 0;
  public void setUp() throws Exception{
      mPool = new ResourcePool(POOL_SIZE);
      System.out.println("Created resource pool of class:" + mPool.getClass().getName());
      mClaimedResources = Collections.synchronizedSet(new HashSet());
    }catch (Throwable e){
      System.err.println("Exception caught within setUp(): ");
      throw new Exception(e);
  public void testClaimResourceSingleThreaded() throws Throwable{
      // Drain and fill the pool twice
      // (second time ensures that items returned to the pool become available again)
      for (int i = 0; i < 2; i++){
        for (int j = 0; j < POOL_SIZE; j++){
          Resource resource = mPool.claimResource();
          if (VERBOSE){
            System.out.println("Claimed resource " + resource);
          assertNotNull("Expected non-null Resource from ResourcePool", resource);
          assertFalse("Resource " + resource + " already claimed.", hasClaimedResource(resource));
          assertNull("Expected empty pool to return null.", mPool.claimResource());

        for (Iterator iterator = mClaimedResources.iterator(); iterator.hasNext();){
          Resource resource = (Resource);
    }catch (Throwable e){
      System.err.println("Throwable caught within test");
      throw e;
  private boolean hasClaimedResource(Resource resource){
    return mClaimedResources.contains(resource);
  private void forgetClaimedResource(Resource resource){
  private void forgetAllClaimedResources(){
  private void storeClaimedResource(Resource resource){
    mMaxClaimedResources = Math.max(mMaxClaimedResources, mClaimedResources.size());
    assertTrue("Expected maximum number of claimed resources at once to be less"+
      " than or equal to pool size",
    mMaxClaimedResources <= POOL_SIZE);
  public void testClaimResourcesMultiThreaded() throws Throwable{
      List threads = new ArrayList();
      ThreadGroup group = new ThreadGroup("ResourcePoolTestThreadGroup");
      for (int i = 0; i < NUM_THREADS; i++){
        ResourceConsumerThread thread = new ResourceConsumerThread(group, "TestThread-" + i);
      waitForThreadGroup(group, THREAD_GROUP_WAIT_TIMEOUT);
      for (Iterator iterator = threads.iterator(); iterator.hasNext();){
        ResourceConsumerThread thread = (ResourceConsumerThread);
        assertTrue("Expected Thread " + thread.getName() + 
          " to complete (probably a bug in the test).",
        Throwable t = thread.getThrowable();
        if (t != null){
          throw t;
    }catch (Throwable e){
      System.err.println("Throwable caught within test");
      throw e;
  private void waitForThreadGroup(ThreadGroup group, long maxWait){
    System.out.println("Max wait time for all threads to finish: " 
      + THREAD_GROUP_WAIT_TIMEOUT/1000 + " seconds.");
    long start = System.currentTimeMillis();
    boolean allDone = false;
    while ((System.currentTimeMillis() - start) < maxWait){
      if (group.activeCount() == 0){
        allDone = true;
        break WAITING;
    if (!allDone){
      throw new RuntimeException("Not all threads completed in time");
  public void tearDown() throws Exception{
      System.out.println("Maximum number of resources claimed at any one time was: "
        + mMaxClaimedResources);
      mMaxClaimedResources = 0;
    }catch (Throwable e){
      System.err.println("Exception caught within tearDown(): ");
      throw new Exception(e);
  class ResourceConsumerThread extends Thread{
    private Throwable mThrowable = null;
    private boolean mDone = false;
    public ResourceConsumerThread(ThreadGroup group, String name){
      super(group, name);
    public Throwable getThrowable(){
      return mThrowable;
    public boolean isDone(){
      return mDone;
    public void run(){
      Resource resource = null;
        for (int i = 0; i < NUM_ITERATIONS_PER_THREAD; i++){
          // Claim a resource
          resource = mPool.claimResource();
          if (!EMPTY_POOL_RETURNS_NULL){
            assertNotNull("Expected non-null Resource from claimResource()", resource);
          if (resource != null){
              assertFalse("Resource " + resource + " already claimed.", hasClaimedResource(resource));
          // Hold the resource
          long sleepTime = Math.abs(sRandom.nextLong()) % MAX_SLEEP_MS;
          if (VERBOSE){
            System.out.println("Thread " + getName() + " sleeping for " 
              + sleepTime + "ms.");
          // Release the resource
          if (resource != null){
            resource = null;
      }catch (Throwable e){
        mThrowable = e;
          if (resource != null){
            resource = null;
        mDone = true;
        if (VERBOSE){
          System.out.println("Thread " + getName() + " completed.");

67.8.7 Evaluation

  • On the surface, this is a very simple coding problem. But it can reveal a great deal more than strictly coding skills.
  • Note how quickly the candidate realizes that the problem is underspecified (i.e., it doesn’t state what to do if the pool is empty).
  • Note how readily the candidate comes up with alternate solutions for handling an empty pool.
  • The choice of data structures is important; a good candidate will instinctively steer clear of a solution that requires them to iterate through the pool in order to find a suitable Resource to return.
  • Pay attention to the candidate’s attitude when they hear that you’re actually going to compile and test their code. Some candidates are put off by having to write real code in an interview; better candidates look forward to the challenge, and enjoy coding anyway.

67.9 Refactor Rock, Paper, Scissors

Suitable for: Mid-Senior Level.
Time: 15-45 minutes
Format: Phone-Screen
Skills: Debugging, refactoring, asking clarifying questions and coding to spec.

To initialize a CoderPad for this problem, go to and select iTunes: Refactor Rock, Paper, Scissors and press Create Pad With Question.

67.9.1 The Problem

In a nutshell, the task is to refactor the code for the Rock, Paper, Scissors game.

The Game class runs the game loop.

  • First, it instantiates 2 players and then for each iteration of the loop.
  • Uses the Player class to randomly select one of the 3 possible options (Rock, Paper, Scissors) for each player
  • Runs the evaluation set of if-then-else conditions to decide which player wins,
  • Increments the number of wins for the winner.

The loop ends when one of the player reaches 3 wins.

Stress to the candidate that the goals are to have code that is maintainable and extensible.

67.9.2 For the Interviewer

Encourage the candidate to think aloud about what they are seeing is wrong with the code and how to improve it.

67.9.3 Hints

  • Where is there redundancy?
  • Where is the code hard to maintain?

67.9.4 Followup Questions

Once the candidate comes up with the first pass solution, you can ask:

  • Can you add a 4th option to the game, e.g. “Chainsaw”, which will beat all of the other options when it’s randomly selected.
  • And once the candidate implements the “Chainsaw” extension, ask them to introduce another option, e.g. “Fire”, which will beat the Paper and the Chainsaw but not the “Rock” and “Scissors” (i.e. chainsaw has some electric component and cables which will get destroyed by “Fire”).

67.9.5 Solution

Sample solution.

static class Game {  
    enum Choice {  
      static Choice getRandom() {  
        return Choice.values()[(int)(Math.random() * Choice.values().length)];  
    // "Beats" relationship  
    private final Map<Choice, Set<Choice>> beats = new HashMap<>();  
    // config  
    private final int numTotalWins;  
    Game(int numTotalWins) {  
      this.numTotalWins = numTotalWins;  
      // setup game rules  
      beats.put(Choice.ROCK, new HashSet<>(Arrays.asList( Choice.SCISSORS, Choice.FIRE)));  
      beats.put(Choice.PAPER, new HashSet<>(Arrays.asList( Choice.ROCK)));  
      beats.put(Choice.SCISSORS, new HashSet<>(Arrays.asList( Choice.PAPER, Choice.FIRE)));  
      beats.put(Choice.FIRE, new HashSet<>(Arrays.asList( Choice.PAPER, Choice.CHAINSAW)));  
      beats.put(Choice.CHAINSAW, new HashSet<>(Arrays.asList( Choice.ROCK,
        Choice.PAPER, Choice.SCISSORS)));  
    void play() {  
      int roundsPlayed = 0;    // Number of rounds played  
      int numDraws = 0;   
      int p1Wins = 0;  
      int p2Wins = 0;  
      while(p1Wins<numTotalWins && p2Wins<numTotalWins) {  
        System.out.println("***** Round: " +  
                           roundsPlayed + " *********************\n");  
        System.out.println("Number of Draws: "+   
                           numDraws + "\n");  
        Choice p1Choice = Choice.getRandom();  
        Choice p2Choice = Choice.getRandom();  
        System.out.println("Player 1: " + p1Choice +   
                           "\t Player 1 Total Wins: " + p1Wins);  
        System.out.println("Player 2: " + p2Choice+   
                           "\t Player 2 Total Wins: " + p2Wins);  
        if( beats.get(p1Choice).contains(p2Choice) ) {  
        } else if( beats.get(p2Choice).contains(p1Choice) ) {  
        } else {  
      System.out.println("GAME OVER");                  
  public static void main(String args[]) {        
    Game game = new Game(3);;        

67.9.6 Evaluation

Rate the candidate based on the following scale (from 2-4): 2.0

At the minimum the candidate should realize that the if-then-else condition in the main loop is spaghetti code is difficult to extend/maintain.

  • Will usually detect there is redundant state in the Player class (just 1 field is needed to keep the number of wins) and will fix it, but will not pay attention to the naming of the setWins method, which should actually be called something like incrementWins, since it’s not a “setter”.
  • Will try to refactor the if-then-else condition using a partial ordering of the set of the options, and then comparing the options taken by player 1 and player 2.
  • Then, may realize the partial ordering is actually not good, since the options form a ring in the “is beaten by” relationship. To continue may come up with coding a special case using an “if” statement to “close the ring” but then they’ll keep adding hacks
  • When asked for the extensions, will keep adding “if” conditions. 3.0

Candidates can come up with a solution that works but:

  • In addition to remove redundant state in Player class, may rename the setWins method so its semantics is better self-documented.
  • Will use some kind of data-driven lookup structure to model the “is beaten by” relationship. It may not come up with a generic solution during the first pass, but should arrive to one that supports the extended questions, once asked. Once refactored, the code should have replaced all the if-then-else conditions by a check to evaluate the “draw” or a lookup against the “is beaten by” data structure to decide which player wins.
  • Regarding the implementation of the “playerChoice” it may still leave it as is, having to maintain the switch statement and the strings for “rock”, “paper”, “scissors” in different places in the code, instead of in a single place.
  • The candidate may not pay attention to other concerns, such as “does the playerChoice method really belongs in the Player class?” 4.0

Candidates come up with a solution that:

In addition to all 3.0 requirements, will

  • The extensions will be implemented by populating new cases in the data structure, never by changing logic.
  • Centralize the knowledge about the options of the game and their “is beaten by” relationship in a single place. e.g. they may use an enum to model the Choice, and then redefine “playerChoice” method to use the Enum::values() method to pick one randomly instead of a explicit switch statement. The lookup data structure will be populated with values from the enum and the “playerChoice” method will not have to be modified when new choices are added.
  • May decide to move the playerChoice method to a different place, since the current implementation doesn’t really depend on the Player state. Potential place: static method on the Choice enum, although a static method in the Game class may make sense too, as far as the access to the set of Choices is public (e.g. Enum::values() will, in case a choice is modeled as an enum)
  • May totally ditch the Player class, since it’s not adding any value for the current functionality (number of wins can be kept in a local variable in the main loop).

67.10 Rolling Window

Suitable for: Mid-Senior Level Backend Engineer
Time: 45 minutes
Format: In-Person
Skills: Data Structures and Problem Solving

67.10.1 The Problem

You have a infinite stream of data flowing through your system. Each data-point/event is defined as a message.

You cannot possibly store and process all the data in the stream. So, you define a window on the stream which holds K milliseconds worth of messages. Implement this window interface with the following operations:

Constraints: * addMsg should be fast. * getMsg and getAverage cannot be prohibitively slow.

67.10.2 For the Interviewer

  • The size of the window is given at instantiation of the class (K milliseconds) and does not change.
  • The constraints are purposely vague (fast, slow). Candidates should be able to ask clarifying questions and come up with equivalent values in terms of time complexity.
    • Fast -> O(1)
    • Not Slow -> Better than O(N), where N = number of messages. O(log N) or O(K) is acceptable.
  • If the candidate proposes a naive hash-map solution, add that removal of old values from the system should also be fast.
    • Re-iterate that the system :
      Cannot store all the incoming messages
      Has enough capacity to store messages for a window and a bit more.

67.10.3 Hints

  • Bucket the keys rather than storing them individually.
  • The most common way to do this is by using a Hash table with a timestamp as key.
  • A circular buffer approach will eliminate the need to do removal. However, some logic is needed to read the correct slots (check if timestamp is beyond the window) when reading.
  • Each entry in the structure above contains another hash table with mapping keys -> messages for that timestamp. We also store the average individually for each timestamp.

67.10.4 Followup Questions

  • If the candidate uses a map of maps approach, ask him whether the cleanup should be done by a background thread in one of the client requests.
  • Ask about thread-safety of the solution

67.10.5 Solution Solution 2 : Use a Circular Buffer

public class RollingWindow implements Window {
    static class ArrayElement {
        private long timestamp = 0;
        private Map<String, Message> events = new HashMap<>();
        private long sum = 0;
        public ArrayElement(long currentTime) {
            this.timestamp = currentTime;
        public void incrementSum(int value) {
            sum += value;

    private final ArrayElement[] elements;
    public RollingWindow(int kMilliseconds) {
        elements = new ArrayElement[kMilliseconds];
    private int getArrayIndex(long currentTime) {
        return (int) (currentTime % elements.length);
    public void addMessage(Message msg) {
        long currentTime = System.currentTimeMillis();
        int index = getArrayIndex(currentTime);

        if (elements[index].timestamp != currentTime) {
            elements[index] = new ArrayElement(currentTime);
        elements[index].events.put(msg.getKey(), msg);
    public Message getMessage(String key) {
        long currentTime = System.currentTimeMillis();
                .filter(element -> (currentTime - element.timestamp) <= elements.length)
                .map(element ->
                .map(events -> events.get(key))
    public double getAverage() {
        long total = 0;
        long numElements = 0;
        long currentTime = System.currentTimeMillis();
        for (ArrayElement element : elements) {
            if (currentTime - element.timestamp <= elements.length) {
                total += element.sum;
                numElements +=;
        if (numElements == 0) return 0;
        return ((double) total) / ((double) numElements);

67.10.6 Unit Test

TODO: When appropriate, create a JUnit test or other way to automatically check the candidate’s solution.

67.10.7 Evaluation

  • The minimum for a candidate is to recognize the issues with using a hash-map to solve the problem and be able to describe a better solution.
  • Can come up with a time complexity definition for fast/slow.
  • Candidate comes up with an optimal and correct data structure and implements it correctly.
  • Candidate describes where/how any cleanup of old messages (if necessary) is performed.
  • Candidate spent time clarifying and asking questions and discussing their approach before jumping into the implementation.


TreeMap of (timestamp to KV):

cleanUP(O(LogN)): tailMap(x).clear()

Heap of (timestamp and KV) + HashMap(k->v,time): ??

AddMsg(logN or KLogN) - K是过期node的个数
getMsg(1 or KLogN)

2017(7-9月) 码农类 硕士 - 校园招聘会 - 技术电面 |Otherfresh grad应届毕业生 indeed 刚刚skype面试结束
是一道Expiring Map的题目,楼主之前虽然看过这个,不过思路不清晰,在skype的时候,楼主使用的是自定义数据结构,Node中包含key value以及一个expiretime,这样在put的时候,就可以new一个node同时获得整个node过期的时间,于是当get一个node的时候,我们就将其过期时间和当前时间比较,如果小于当期时间,就从map中删除该key,返回nul,否则就正常返回.最后面试官问,如果大量数据过期怎么办,使用一个PriorityQueue,比较器是根据过期时间来的,这样堆顶就是最早过期的元素,于是我们可以在get的时候或者put的时候,将过期的元素都删除.楼主忘了在put的时候也要加上过滤过期数据的代码了…刚想起来,在最后的时候让分析了put和set的时间复杂度. 然后就结束了.不知道哪位有较好的解法,跪求.

2017(10-12月) 码农类 硕士 - 内推 - 技术电面 |Passfresh grad应届毕业生 一面:
实现一个会过期的Map.有put(key, value, lastTime)和get(key)两个方法.每个k-v对放入Map时附带一个有效时间lastTime,持续到过期前都可以正常get,过期后再get则返回无效标记.
关于lastTime什么数据结构我当时询问了面试官,他表示他也不很用JAVA不是很知道里面用什么时间库,就让我assume一个long getCurrentTime()方法来做了,于是解题中是用long来表示时间.
follow ups: 1.如果你出unit test你会出什么case. 2.时间复杂度. 3.如果要实现一个cleanUp(timeStamp)函数,清除所有在timeStamp时间之前的记录,你会怎么修改你的设计.实现.时间复杂度是多少.
实现一个票数统计器.String getLeader(Collection votes, Long timeStamp) 方法.其中Vote类有两个方法(String getLeader() 和 Long getTimeStamp())表示某一票是投给谁的,什么时候投的. 你要做的method是统计timeStamp之前所有的有效投票,把票数最高的参选人名字返回. follow ups: 1.写一个 List getTopKLeaders(Collections votes, Long timeStamp, int k) 方法,返回某个时间点前统计出来的前k名最强领导人. 2.优化新函数时间复杂度.
1.google家面试都是45分钟,我面到的几场都是围绕单独一个题目环境进行,但有一大几小follow ups循序渐进
2.用的编辑界面是Google Doc.写代码不是很好写,但是你讲解的时候你可以选中文字来highlight,感觉对讨论挺有帮助记得多用用.
10.20 内推
11.20 收到OA (当天完成)
11.22 约一二面 (11.29收到安排时间)
12.5 一二面背靠背.
12.11 约加面 (12.13收到安排时间)
12.18 三轮加面
12.19 通知面试通过送hiring committee

  • 如何测试?

Testing Timed Eviction Testing timed eviction doesn’t have to be painful…and doesn’t actually have to take you two seconds to test a two-second expiration. Use the Ticker interface and the CacheBuilder.ticker(Ticker) method to specify a time source in your cache builder, rather than having to wait for the system clock. …
The primary intent of this method is to facilitate testing of caches with a fake or mock time source.

Hashed and Hierarchical Timing Wheels: Data Structures for the Efficient Implementation of a Timer Facility -

Implementation-wise, this will be done using a hierarchical timer wheel (prototype). This allows add, remove, rescheduling, and expiring to be performed in O(1) time. Instead of using a O(lg n) priority queue, it uses a hierarchy of ring buffers, where each slot represents a time span (second, minute, hour). As the wheels rotate, the entries in the buckets are expired or rescheduled. This cascading effect amortizes the penalty and all operations are run during the cache’s maintenance operation.

2017(10-12月) 码农类 博士 - 网上海投 - Onsite |Other在职跳槽
Uber seattle site onsite..
第一轮: rateLimiter… 面试官讲故事太多了,刚开始被绕进去了,最后才知道实际上是rate Limter, MAP+Queue的实现…这轮目测要跪
第二轮: 小印哥们, 各种parenthesis 变种,LC基本括号相关题follow up了一遍
第三轮: 三哥, LC原题,deecopy Linkedlist with random pointer, 关键是follow up一堆多机器怎么实现copy…这个目测答得不好,地里有经验的可以说说.
第四轮,又是Rate limiter….这次没让面试官讲故事绕进去,但关键一堆follow up scalable , consistent hashing, 没啥把握
补充内容 (2017-10-13 00:13):
楼主已挂,distributed system经验不足..

2017(7-9月) 码农类 硕士 - 内推 - Onsite |Pass在职跳槽
第二轮:Range query sum 2D mutable.上来BIT的方法解释了半天,面试官之前没听说过所以很难理解,最后提示让用O(n) prefixsum array的方法
第三轮:Path Sum I & II, 就是最简单的那个DP,剩了15分钟聊了下天
第四轮:面经题,robot 打扫房间
第五轮:1. design 一个数据结构,里面有key,value,expiration time, 实现get, put, clean(清空已经过时的key)
2. (facebook, 10), (fakebook, 20) -> 给你prefix 和suffix 求以prefix开头和suffix结尾的所有项目中,分数最高的,不用写代码 就是纯design
例如:(fa, ok) -> fakebook; (fac, ok) -> facebook

It seems that many people believed for some reason that freeing expired entries can not be performed in O(1), or even that it requires Omega(N) operations. Using a heap, or other priority queue data structures can obviously give you O(log N), but the patch below aims at O(1). This is achieved by having one bucket for each second, and by putting each entry in a proper bucket by looking at the expiration time. Then at each second we just free elements from the next bucket. This is obviously O(1) amortized time, but it can happen that you have a lot of elements that expire at the very same moment, so the patch offers a fixed limit for number of operations that you are willing to perform per one request, to make the garbage collection run smoother.

  • TreeMap removing all keys greater than a certain key

The same treemap above:

so it is a half open interval removal.

  • C++ Map

  • Thread Safe Set
  1. ConcurrentHashMap backed ConcurrentHashSet
  1. SynchronizedSet

67.12 URL Shortener

Suitable for: Mid-Senior Level.
Time: 15-45 minutes
Format: Phone-Screen
Skills: System design, knowledge of algorithms, asking clarifying questions.

There is no code for this question, it is intended as a way to judge a candidate’s problem decomposition and design skills. With particular attention to the scalability of the proposed solution.

67.12.1 The Problem

First ask the candidate if they are familiar with URL shorteners.

If they say yes, ask them to describe what they do to confirm.

If not, describe the problem domain:

In essence, a web service that when given a URL like
"", it will provide back another shortened URL
like, for example, "". The domain is a predefined prefix and
the path is unique to the original URL. Now additionally, if the service is
provided the shortened URLs, it will return the original URL. Once they get
this, ask the candidate to go thru how they would design a system that does

67.12.2 For the Interviewer

Once you have laid out the problem, stop talking and let the candidate drive the conversation.

In case they ask, some scenario answers:

  • It should be a process that communicates via HTTP.
  • Ignore all the redirect stuff that real shorteners do. Focus on the actual internal work involved.
  • Assume some scale here - 10k shortens per sec. 100k expands per sec. Importantly, mention that a single process cannot handle this. We want a distributed solution.
  • The system design should consider that nodes may fail.
  • Two URLs should never map to the same shortened one and vice versa.
  • URLs can stick around forever. There is no requirement to expire them - unless they prefer to do so.

What you want is for them to try and focus on the system design of the two basic functions - namely shorten and expand (or any similar names they come up with). Of the two, the shorten function is more complex and is more important to drill into. They should consider the shorten algorithm and how it persists data so expand can use it.

67.12.3 Followup Questions

Ask the candidate some of these questions to probe their solution and thinking:

  • How do you guarantee that there are no collisions on your shorten function?
  • How is load balancing accomplished among all the nodes?
  • What kind of data store would you use as the backend database and why?

67.12.4 Solution

Here is a possible answer that satisfies the given constraints:

Start with the two functions, shorten and expand most of the focus will be on the shorten design: Shorten

Partition all things. Decide on a number of partitions this system will have. This is your maximum scale factor (# of machines). 2048, 1024, 512, etc (N) can be reasonable numbers. Each partition contains a int range counter.

Then MAX_INT * N is your theoretical max number of shortened URLs supported.

On startup, a process is “assigned” partitions via some coordination system (Zookeeper). If there is only one process, it would get all N partitions. if the second one joins, it would be re-balanced to get N / 2, etc.

For each partition, the process would then recover the maximum count already used in that partition from a durable storage to jump to where it left off. Now it can receive traffic. This may be load-balanced in any convenient way. Any process can receive any request.

When a process gets a shorten request, it would then (via round-robin / hash / etc) choose a partition to use to serve that traffic. It then increments the count on that partition, puts the partition id + count together into some number: some bytes are the partition Id, some are the counter. Then it converts it into some base encoding that is reasonable like Base62.

It then stores the ID and URL in some database so it can be queried via this ID. It should be put in a way so that it is easy to query the max value already set. This can be easy in some DBs by using a different table per partition and finding the max ID. In a KV store, you’d need to store the max ID per partition somewhere in addition to the normal data (in a KV store, this additional write should still be low impact).

Once complete, send back the URL prefix + the generated URL ID. Expand

Pull the URL ID from the URL, decode it back to a number. Use it to find the data.

67.12.6 Evaluation

Score this on a scale from 1-4: 1.0

At the minimum the candidate should realize that there are two main things to consider - shortening URLs and expanding them. Their solution functionally works, but:

  • Doesn’t really make sense
  • Would have immediate scaling issues.
  • URL shortening collisions may happen at a reasonable probability.
  • They may run out of possible shortenings relatively quickly.
  • Use an unfriendly output of the shortening - like a UUID. It should be short (4-10 characters max). 2.0

Candidates can come up with a solution that works but:

  • Requires coordination on each shorten attempt or multiple reads/writes.
  • Shortened URLs may collide at a very low probability.
  • Chooses a specific datastore for the URLs that cannot scale linearly for this type of system.
  • They cannot explain possible failure scenarios (app and/or datastores) and how the system would respond in a reasonable way.
  • It is non-trivial to expand the cluster. 3.0

Candidates come up with a solution that: - Does not require coordination between nodes during shorten/expand. - Shortened URLs can never collide. - Shorten requires no reads. It is a single write. (depends on DB used. An additional write may be needed). - Expand is a single read. 4.0

Candidates have a 3.0 solution, but can also:

  • Explain and show how the system can be available on node failures (database failures, node failures, etc), for example:
    • How do we guarantee that only one node owns a partition and receives traffic at a given time considering different network partitions / stuckness?
    • What do we do if the database is irresponsive during shorten? During expand?
  • Explain how we can have the highest level of availability for the the expand stage? For the shorten stage?
  • Explain and show how the system can be expanded easily (while running).
  • Can elaborate on the specific database technology used and how they would use it in this particular problem.

67.12.7 734. Sentence Similarity

Given two sentences words1, words2 (each represented as an array of strings), and a list of similar word pairs pairs, determine if two sentences are similar.

For example, “great acting skills” and “fine drama talent” are similar, if the similar word pairs are pairs = [[“great”, “fine”], [“acting”,“drama”], [“skills”,“talent”]].

Note that the similarity relation is not transitive. For example, if “great” and “fine” are similar, and “fine” and “good” are similar, “great” and “good” are not necessarily similar.

However, similarity is symmetric. For example, “great” and “fine” being similar is the same as “fine” and “great” being similar.

Also, a word is always similar with itself. For example, the sentences words1 = [“great”], words2 = [“great”], pairs = are similar, even though there are no specified similar word pairs.

Finally, sentences can only be similar if they have the same number of words. So a sentence like words1 = [“great”] can never be similar to words2 = [“doubleplus”,“good”].

Note: The length of words1 and words2 will not exceed 1000.
The length of pairs will not exceed 2000.
The length of each pairs[i] will be 2.
The length of each words[i] and pairs[i][j] will be in the range [1, 20].

67.12.8 737. Sentence Similarity II

Given two sentences words1, words2 (each represented as an array of strings), and a list of similar word pairs pairs, determine if two sentences are similar.

For example, words1 = [“great”, “acting”, “skills”] and words2 = [“fine”, “drama”, “talent”] are similar, if the similar word pairs are pairs = [[“great”, “good”], [“fine”, “good”], [“acting”,“drama”], [“skills”,“talent”]].

Note that the similarity relation is transitive. For example, if “great” and “good” are similar, and “fine” and “good” are similar, then “great” and “fine” are similar.

Similarity is also symmetric. For example, “great” and “fine” being similar is the same as “fine” and “great” being similar.

Also, a word is always similar with itself. For example, the sentences words1 = [“great”], words2 = [“great”], pairs = are similar, even though there are no specified similar word pairs.

Finally, sentences can only be similar if they have the same number of words. So a sentence like words1 = [“great”] can never be similar to words2 = [“doubleplus”,“good”].