Image courtesy of the author
Landing a job at Amazon is a dream for many developers around the globe. Amazon is one of the largest companies in the world, with a workforce over half a million strong. Amazon is hiring at a rapid rate with a unique hiring process that emphasizes company culture and leadership principles. Today, I will walk through everything you need to crack an Amazon interview, including coding questions and a step-by-step preparation guide.
Today we will go over the following:
Image courtesy of the author
You are given an array of positive numbers from 1
to n
, such that all numbers from 1
to n
are present except one number x
. You have to find x
. The input array is not sorted. Look at the below array and view the solution in Python. Click here to view the solution in C++, Java, JavaScript, and Ruby.
def find_missing(input):
# calculate sum of all elements
# in input list
sum_of_elements = sum(input)
# There is exactly 1 number missing
n = len(input) + 1
actual_sum = (n * ( n + 1 ) ) / 2
return actual_sum - sum_of_elements
def test(n):
missing_element = random.randint(1, n)
v = []
for i in range(1, n):
if i != missing_element:
v.append(i)
actual_missing = find_missing(v)
print("Expected Missing = ", missing_element, " Actual Missing = ", actual_missing)
assert missing_element == actual_missing
def main():
for n in range(1, 10):
test(1000000)
main()
Runtime Complexity: Linear, O(n)
Memory Complexity: Constant, O(1)
A naive solution is to simply search for every integer between 1
and n
in the input array, stopping the search as soon as there is a missing number. But we can do better. Here is a linear, O(n) solution that uses the arithmetic series sum formula. Here are the steps to find the missing number:
sum_of_elements
of all the numbers in the array. This would require a linear scan, O(n).expected_sum
of first n
numbers using the arithmetic series sum formulaexpected_sum - sum_of_elements
, is the missing number in the array.Given an array of integers and a value, determine if there are any two integers in the array whose sum is equal to the given value. Return true
if the sum exists and return false
if it does not. Consider this array and the target sums. Click here to view the solution in C++, Java, JavaScript, and Ruby.
def find_sum_of_two(A, val):
found_values = set()
for a in A:
if val - a in found_values:
return True
found_values.add(a)
return False
v = [5, 7, 1, 2, 8, 4, 3]
test = [3, 20, 1, 2, 7]
for i in range(len(test)):
output = find_sum_of_two(v, test[i])
print("find_sum_of_two(v, " + str(test[i]) + ") = " + str(output))
Runtime Complexity: Linear, O(n)
Memory Complexity: Linear, O(n)
You can use the following algorithm to find a pair that add up to the target (say, val
).
scan
, for every element e
in the array, we check if val - e
is present in the hash set, i.e., val - e
is already visited.val - e
is found in the hash set, it means there is a pair (e
, val - e
) in the array whose sum is equal to the given val
.false
.Given two sorted linked lists, merge them so that the resulting linked list is also sorted. Consider two sorted linked lists and the merged list below them as an example. Click here to view the solution in C++, Java, JavaScript, and Ruby.
def merge_sorted(head1, head2):
# if both lists are empty then merged list is also empty
# if one of the lists is empty then other is the merged list
if head1 == None:
return head2
elif head2 == None:
return head1
mergedHead = None;
if head1.data <= head2.data:
mergedHead = head1
head1 = head1.next
else:
mergedHead = head2
head2 = head2.next
mergedTail = mergedHead
while head1 != None and head2 != None:
temp = None
if head1.data <= head2.data:
temp = head1
head1 = head1.next
else:
temp = head2
head2 = head2.next
mergedTail.next = temp
mergedTail = temp
if head1 != None:
mergedTail.next = head1
elif head2 != None:
mergedTail.next = head2
return mergedHead
array1 = [2, 3, 5, 6]
array2 = [1, 4, 10]
list_head1 = create_linked_list(array1)
print("Original1:")
display (list_head1)
list_head2 = create_linked_list(array2)
print("\nOriginal2:")
display (list_head2)
new_head = merge_sorted(list_head1, list_head2)
print("\nMerged:")
display(new_head)
Runtime Complexity: Linear, O(m+n) where m and n are lengths of both linked lists
Memory Complexity: Constant, O(1)
Maintain a head and a tail pointer on the merged linked list. Then choose the head of the merged linked list by comparing the first node of both linked lists. For all subsequent nodes in both lists, you choose the smaller current node, link it to the tail of the merged list, and move the current pointer of that list one step forward.
Continue this while there are some remaining elements in both the lists. If there are still some elements in only one of the lists, you link this remaining list to the tail of the merged list. Initially, the merged linked list is NULL
.
Compare the value of the first two nodes and make the node with the smaller value the head node of the merged linked list. In this example, it is 4
from head1
. Since it’s the first and only node in the merged list, it will also be the tail. Then move head1
one step forward.
You are given a linked list where the node has two pointers. The first is the regular next
pointer. The second pointer is called arbitrary_pointer
, and it can point to any node in the linked list. Your job is to write code to make a deep copy of the given linked list. Here, deep copy means that any operations on the original list should not affect the copied list. Click here to view the solution in C++, Java, JavaScript, and Ruby.
def deep_copy_arbitrary_pointer(head):
if head == None:
return None
current = head;
new_head = None
new_prev = None
ht = dict()
# create copy of the linked list, recording the corresponding
# nodes in hashmap without updating arbitrary pointer
while current != None:
new_node = LinkedListNode(current.data)
# copy the old arbitrary pointer in the new node
new_node.arbitrary = current.arbitrary;
if new_prev != None:
new_prev.next = new_node
else:
new_head = new_node
ht[current] = new_node
new_prev = new_node
current = current.next
new_current = new_head
# updating arbitrary pointer
while new_current != None:
if new_current.arbitrary != None:
node = ht[new_current.arbitrary]
new_current.arbitrary = node
new_current = new_current.next
return new_head
def create_linked_list_with_arb_pointers(length):
head = create_random_list(length)
v = []
temp = head
while temp != None:
v.append(temp)
temp = temp.next
for i in range(0, len(v)):
j = random.randint(0, len(v) - 1)
p = random.randint(0, 100)
if p < 75:
v[i].arbitrary = v[j]
return head
Runtime Complexity: Linear, O(n)
Memory Complexity: Linear, O(n)
This approach uses a map to track arbitrary nodes pointed by the original list. You will create a deep copy of the original linked list (say list_orig
) in two passes.
arbitrary_pointer
in the new list. Also, keep updating the map with entries where the key is the address to the old node and the value is the address of the new node.Given the root of a binary tree, display the node values at each level. Node values for all levels should be displayed on separate lines. Let’s take a look at the below binary tree. Click here to view the solution in C++, Java, JavaScript, and Ruby.
# Using two queues
def level_order_traversal_1(root):
if root == None:
return
queues = [deque(), deque()]
current_queue = queues[0]
next_queue = queues[1]
current_queue.append(root)
level_number = 0
while current_queue:
temp = current_queue.popleft()
print(str(temp.data) , end = " ")
if temp.left != None:
next_queue.append(temp.left)
if temp.right != None:
next_queue.append(temp.right)
if not current_queue:
print()
level_number += 1
current_queue = queues[level_number % 2]
next_queue = queues[(level_number + 1) % 2]
print()
arr = [100,50,200,25,75,350]
root = create_BST(arr)
print("InOrder Traversal:", end = "")
display_inorder(root)
print("\nLevel Order Traversal:\n", end = "")
level_order_traversal_1(root)
Runtime Complexity: Linear, O(n)
Memory Complexity: Linear, O(n)
Here you are using two queues: current_queue
and next_queue
. You push the nodes in both queues alternately based on the current level number.
You’ll dequeue nodes from the current_queue
, print the node’s data, and enqueue the node’s children to the next_queue
. Once the current_queue
becomes empty, you have processed all nodes for the current level_number
. To indicate the new level
, print a line break (\n
), swap the two queues, and continue with the above-mentioned logic.
After printing the leaf nodes from the current_queue
, swap current_queue
and next_queue
. Since the current_queue
would be empty, you can terminate the loop.
#programming #interview #amazon #job-interview #coding-interviews