304 North Cardinal St.
Dorchester Center, MA 02124
Uber’s coding interviews are hard, but not impossible. Like anything else, it just takes practice. We’ll walk you through it, step by step.
You have a list of integers, and for each index you want to find the product of every integer except the integer at that index.
Write a function get_products_of_all_ints_except_at_index() that takes a list of integers and returns a list of the products.
For example, given:
[1, 7, 3, 4]
your function would return:
[84, 12, 28, 21]
[7 * 3 * 4, 1 * 3 * 4, 1 * 7 * 4, 1 * 7 * 3]
Here’s the catch: You can’t use division in your solution!
Does your function work if the input list contains zeroes? Remember—no division.
We can do this in O(n)O(n) time and O(n)O(n) space!
We only need to allocate one new list of size nn.
A brute force approach would use two loops to multiply the integer at every index by the integer at every nested_index, unless index == nested_index.
This would give us a runtime of O(n^2)O(n2). Can we do better?
Well, we’re wasting a lot of time doing the same calculations. As an example, let’s take:
# Input list
[1, 2, 6, 5, 9] # List of the products of all integers # except the integer at each index: [540, 270, 90, 108, 60] # [2 * 6 * 5 * 9, 1 * 6 * 5 * 9, 1 * 2 * 5 * 9, 1 * 2 * 6 * 9, 1 * 2 * 6 * 5]
We’re doing some of the same multiplications two or three times!
Or look at this pattern:
We’re redoing multiplications when instead we could be storing the results! This would be a great time to use a greedy ↴ approach. We could store the results of each multiplication highlighted in blue, then just multiply by one new integer each time.
So in the last highlighted multiplication, for example, we wouldn’t have to multiply 1*2*61∗2∗6 again. If we stored that value (1212) from the previous multiplication, we could just multiply 12*512∗5.
Can we break our problem down into subproblems so we can use a greedy approach?
Let’s look back at the last example:
What do all the highlighted multiplications have in common?
They are all the integers that are before each index in the input list ([1, 2, 6, 5, 9][1,2,6,5,9]). For example, the highlighted multiplication at index 3 (1*2*61∗2∗6) is all the integers before index 3 in the input list.
Do all the multiplications that aren’t highlighted have anything in common?
Yes, they’re all the integers that are after each index in the input list!
Knowing this, can we break down our original problem to use a greedy approach?
The product of all the integers except the integer at each index can be broken down into two pieces:
To start, let’s just get the product of all the integers before each index.
How can we do this? Let’s take another example:
# Input list
[3, 1, 2, 5, 6, 4] # Multiplication of all integers before each index # (we give index 0 a value of 1 since it has no integers before it) [1, 3, 3 * 1, 3 * 1 * 2, 3 * 1 * 2 * 5, 3 * 1 * 2 * 5 * 6] # Final list of the products of all the integers before each index [1, 3, 3, 6, 30, 180]
Notice that we’re always adding one new integer to our multiplication for each index!
So to get the products of all the integers before each index, we could greedily store each product so far and multiply that by the next integer. Then we can store that new product so far and keep going.
So how can we apply this to our input list?
Let’s make a list products_of_all_ints_before_index:
products_of_all_ints_before_index = [None] * len(int_list)
# For each integer, find the product of all the integers # before it, storing the total product so far each time product_so_far = 1 for i in xrange(len(int_list)): products_of_all_ints_before_index[i] = product_so_far product_so_far *= int_list[i]
So we solved the subproblem of finding the products of all the integers before each index. Now, how can we find the products of all the integers after each index?
It might be tempting to make a new list of all the values in our input list in reverse, and just use the same function we used to find the products before each index.
Is this the best way?
This method will work, but:
Is there a cleaner way to get the products of all the integers after each index?
We can just walk through our list backwards! So instead of reversing the values of the list, we’ll just reverse the indices we use to iterate!
products_of_all_ints_after_index = [None] * len(int_list)
product_so_far = 1 for i in xrange(len(int_list) - 1, -1, -1): products_of_all_ints_after_index[i] = product_so_far product_so_far *= int_list[i]
Now we’ve got products_of_all_ints_after_index, but we’re starting to build a lot of new lists. And we still need our final list of the total products. How can we save space?
Let’s take a step back. Right now we’ll need three lists:
To get the first one, we keep track of the total product so far going forwards, and to get the second one, we keep track of the total product so far going backwards. How do we get the third one?
Well, we want the product of all the integers before an index and the product of all the integers after an index. We just need to multiply every integer in products_of_all_ints_before_index with the integer at the same index in products_of_all_ints_after_index!
Let’s take an example. Say our input list is [2, 4, 10][2,4,10]:
We’ll calculate products_of_all_ints_before_index as:
And we’ll calculate products_of_all_ints_after_index as:
If we take these lists and multiply the integers at the same indices, we get:
And this gives us what we’re looking for—the products of all the integers except the integer at each index.
Knowing this, can we eliminate any of the lists to reduce the memory we use?
Yes, instead of building the second list products_of_all_ints_after_index, we could take the product we would have stored and just multiply it by the matching integer in products_of_all_ints_before_index!
So in our example above, when we calculated our first (well, “0th”) “product after index” (which is 40), we’d just multiply that by our first “product before index” (1) instead of storing it in a new list.
How many lists do we need now?
Just one! We create a list, populate it with the products of all the integers before each index, and then multiply those products with the products after each index to get our final result!
products_of_all_ints_before_index now contains the products of all the integers before and after every index, so we can call it products_of_all_ints_except_at_index!
Almost done! Are there any edge cases we should test?
What if the input list contains zeroes? What if the input list only has one integer?
We’ll be fine with zeroes.
But what if the input list has fewer than two integers?
Well, there won’t be any products to return because at any index there are no “other” integers. So let’s raise an exception.
To find the products of all the integers except the integer at each index, we’ll go through our list greedily ↴ twice. First we get the products of all the integers before each index, and then we go backwards to get the products of all the integers after each index.
When we multiply all the products before and after each index, we get our answer—the products of all the integers except the integer at each index!
if len(int_list) < 2: raise IndexError('Getting the product of numbers at other ' 'indices requires at least 2 numbers') # We make a list with the length of the input list to # hold our products products_of_all_ints_except_at_index = [None] * len(int_list) # For each integer, we find the product of all the integers # before it, storing the total product so far each time product_so_far = 1 for i in xrange(len(int_list)): products_of_all_ints_except_at_index[i] = product_so_far product_so_far *= int_list[i] # For each integer, we find the product of all the integers # after it. since each index in products already has the # product of all the integers before it, now we're storing # the total product of all other integers product_so_far = 1 for i in xrange(len(int_list) - 1, -1, -1): products_of_all_ints_except_at_index[i] *= product_so_far product_so_far *= int_list[i] return products_of_all_ints_except_at_index
O(n)O(n) time and O(n)O(n) space. We make two passes through our input a list, and the list we build always has the same length as the input list.
What if you could use division? Careful—watch out for zeroes!
Another question using a greedy ↴ approach. The tricky thing about this one: we couldn’t actually solve it in one pass. But we could solve it in two passes!
This approach probably wouldn’t have been obvious if we had started off trying to use a greedy approach.
Instead, we started off by coming up with a slow (but correct) brute force solution and trying to improve from there. We looked at what our solution actually calculated, step by step, and found some repeat work. Our final answer came from brainstorming ways to avoid doing that repeat work.
So that’s a pattern that can be applied to other problems:
Start with a brute force solution, look for repeat work in that solution, and modify it to only do that work once.
In our experience, we suggest you solve this Practice Questions for the Uber Interview and gain some new skills from Professionals completely free and we assure you will be worth it.
If you are stuck anywhere between any coding problem, just visit Queslers to get the Practice Questions for the Uber Interview
I hope this Practice Questions for the Uber Interview would be useful for you to learn something new from this problem. If it helped you then don’t forget to bookmark our site for more Coding Solutions.
This Problem is intended for audiences of all experiences who are interested in learning about Data Science in a business context; there are no prerequisites.
More Coding Solutions >>