We stop when we run out of customer orders in our served_orders. So that's our base case: when we've checked all customer orders in served_orders, we return True because we know all of the customer orders have been "accounted for."

def is_first_come_first_served(take_out_orders, dine_in_orders, served_orders): # Base case if len(served_orders) == 0: return True # If the first order in served_orders is the same as the # first order in take_out_orders # (making sure first that we have an order in take_out_orders) if len(take_out_orders) and take_out_orders[0] == served_orders[0]: # Take the first order off take_out_orders and served_orders and recurse return is_first_come_first_served(take_out_orders[1:], dine_in_orders, served_orders[1:]) # If the first order in served_orders is the same as the first # in dine_in_orders elif len(dine_in_orders) and dine_in_orders[0] == served_orders[0]: # Take the first order off dine_in_orders and served_orders and recurse return is_first_come_first_served(take_out_orders, dine_in_orders[1:], served_orders[1:]) # First order in served_orders doesn't match the first in # take_out_orders or dine_in_orders, so we know it's not first-come, first-served else: return False

This solution will work. But we can do better.

Before we talk about optimization, note that our inputs are probably pretty small. This function will take hardly any time or space, even if it could be more efficient. In industry, especially at small startups that want to move quickly, optimizing this might be considered a "premature optimization." Great engineers have both the skill to see how to optimize their code and the wisdom to know when those optimizations aren't worth it. At this point in the interview I recommend saying, "I think we can optimize this a bit further, although given the nature of the input this probably won't be very resource-intensive anyway...should we talk about optimizations?"

Suppose we do want to optimize further. What are the time and space costs to beat? This function will take time and additional space.

Whaaaaat? Yeah. Take a look at this snippet:

return is_first_come_first_served(take_out_orders[1:], dine_in_orders, served_orders[1:])

In particular this expression:

take_out_orders[1:]

That's a slice, and it costs time and space, where m is the size of the resulting list. That's going to determine our overall time and space cost here—the rest of the work we're doing is constant space and time.

In our recursing we'll build up n frames on the call stack. Each of those frames will hold a different slice of our original served_orders (and take_out_orders and dine_in_orders, though we only slice one of them in each recursive call).

So, what's the total time and space cost of all our slices?

If served_orders has n items to start, taking our first slice takes n-1 time and space (plus half that, since we're also slicing one of our halves—but that's just a constant multiplier so we can ignore it). In our second recursive call, slicing takes n-2 time and space. Etc.

So our total time and space cost for slicing comes to:

(n - 1) + (n - 2) + ... + 2 + 1

This is a common series that turns out to be .

We can do better than this time and space cost. One way we could to that is to avoid slicing and instead keep track of indices in the list:

def is_first_come_first_served(take_out_orders, dine_in_orders, served_orders, take_out_orders_index=0, dine_in_orders_index=0, served_orders_index=0): # Base case we've hit the end of served_orders if served_orders_index == len(served_orders): return True # If we still have orders in take_out_orders # and the current order in take_out_orders is the same # as the current order in served_orders if ((take_out_orders_index < len(take_out_orders)) and take_out_orders[take_out_orders_index] == served_orders[served_orders_index]): take_out_orders_index += 1 # If we still have orders in dine_in_orders # and the current order in dine_in_orders is the same # as the current order in served_orders elif ((dine_in_orders_index < len(dine_in_orders)) and dine_in_orders[dine_in_orders_index] == served_orders[served_orders_index]): dine_in_orders_index += 1 # If the current order in served_orders doesn't match # the current order in take_out_orders or dine_in_orders, then we're not # serving in first-come, first-served order. else: return False # The current order in served_orders has now been "accounted for" # so move on to the next one served_orders_index += 1 return is_first_come_first_served( take_out_orders, dine_in_orders, served_orders, take_out_orders_index, dine_in_orders_index, served_orders_index)

So now we're down to time, but we're still taking space in the call stack because of our recursion. We can rewrite this as an iterative function to get that memory cost down to .