Understand the fundamentals of data structures and algorithms, as Amazon interviews often involve complex problem-solving. Practice coding regularly. Check out the previous Amazon coding interview questions and practice well with them.
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 of over half a million strong.
For you to join them, you’ll need to complete their unique interview that combines technical and leadership knowledge.
Today, we’ll walk you through everything you need to crack the Amazon technical interview, including questions and answers for coding problems and a step-by-step tech interview prep guide.
Amazon is a dream company for many developers. To get yourself a job at Amazon, the most important part is to practice your interview skills and apply for the correct position. Make sure you thoroughly look at the job description to ensure that it matches your skill set. The next step is to practice some Amazon Leetcode Questions. Solving Leetcode problems is an exceptional way to practice your coding skills.
Educative has curated a collection of Amazon-specific LeetCode problems to facilitate focused practice. There’s no need to feel overwhelmed by the vast array of over 2000+ LeetCode problems. With this select set, you can practice and develop the logic necessary to tackle all the coding problems typically encountered in Amazon interviews. We’veve also added 40+ commonly asked Amazon coding interview questions for you to practice and improve your skills. For detailed lists of questions curated to help you ace the Amazon coding interviews, Educative-99 and Educative-77 are the perfect places to start.
Answer any Amazon interview question by learning the patterns behind common questions.#
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 give it a try before checking the solution.
def find_missing(nums):# Replace this placeholder return statement with your codereturn -1
View the find the missing number solution in C++, Java, JavaScript, and Ruby.
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. However we can do better. Here is a linear,
Find the sum sum_of_elements
of all the numbers in the array.
Then, find the sum actual_sum
of the first n
numbers using the arithmetic series sum formula
The difference between these, i.e., actual_sum - sum_of_elements
, is the missing number in the array.
Runtime complexity: Linear,
Memory complexity: Constant,
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:
def check_two_sum(arr, t):# Replace this placeholder return statement with your codereturn False
View the sum of two integers solution in C++, Java, JavaScript, and Ruby.
We can use the following algorithm to find a pair that adds up to target
.
Scan the whole array once and store visited elements in a hash set.
During the scan, for every element num
in the array, we check if target - num
is present in the hash set, i.e., target - num
is already visited.
If target - num
is found in the hash set, it means there is a pair (num
, target - num
) in the array whose sum is equal to the given target
, so we return TRUE.
If we have exhausted all elements in the array and found no such pair, the function will return FALSE.
Runtime complexity: Linear,
Memory complexity: Linear,
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.
# class LinkedListNode:# def __init__(self, data, next=None):# self.data = data# self.next = nextdef merge_lists(head1, head2):# Replace this placeholder return statement with your codereturn None
View the merge two sorted linked lists solution in C++, Java, JavaScript, and Ruby.
Given that head1
and head2
are pointing to the head nodes of the given linked lists, respectively, if one of them is None
, return the other as the merged list. Otherwise, perform the following steps:
Compare the data of the first nodes of both lists and initialize the merged_head
to the smaller of the two nodes. Also, initialize merged_tail
as merged_head
. Move the smaller node pointer (head1
or head2
) to the next node.
Perform the following steps until either head1
or head2
is NULL:
Compare the data of the current nodes pointed by head1
and head2
, append the smaller node to merged_tail
, and move the corresponding pointer forward.
Update merged_tail
to the appended node.
After exiting the loop, if either list has remaining nodes, append the remaining nodes to the end of the merged list. Do this by updating the next
pointer of merged_tail
to point to the remaining nodes.
Return merged_head
, which represents the head of the merged list.
Runtime complexity: Linear,
Memory complexity: Constant,
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
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.
# Template for the linked list node class with arbitrary pointer# class LinkedListNode:# # __init__ will be used to make a LinkedListNode type object# def __init__(self, data, next=None, arbitrary=None):# self.data = data# self.next = next# self.arbitrary = arbitrarydef deep_copy_arbitrary_pointer(head):# Replace this placeholder return statement with your codereturn head
View the copy linked list solution in C++, Java, JavaScript, and Ruby.
If the given linked list’s head is NULL, return NULL as the copied list.
Initialize a current
pointer with head
to traverse the original linked list. Also, initialize new_head
and new_prev
pointers to NULL to keep track of the copied list. Additionally, create an empty dictionary (hashmap) named ht
to map nodes in the original and copied lists.
Traverse the original linked list while creating a copy of each node. For each node:
Create a new_node
with the same data as the current
node.
Copy the arbitrary
pointer of the current
node to the new_node
. Therefore, for now, we are pointing the arbitrary
pointer of the new_node
to where it is pointing in the original node.
If new_node
is the first node in the copied list, set new_head
and new_prev
to new_node
. Otherwise, connect the new_node
to the copied list by setting new_prev.next
to new_node
and set new_prev
to new_node
.
Store copied node new_node
in the ht
with the key current
. This will help us point the arbitrary
pointer of the copied nodes to the correct new nodes in the copied list.
For now, the arbitrary
pointers in the new nodes point to nodes in the original list. To update them, we traverse the new list using new_current
, utilize the hashmap ht
to find the corresponding copied node for each original node pointed to by the arbitrary
pointer, and then update the arbitrary
pointer of the current node to point to this copied node.
Once all arbitrary pointers are updated, return new_head
, which represents the head of the copied list.
Runtime complexity: Linear,
Memory complexity: Linear,
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.
# Definition for a binary tree node# class TreeNode:# def __init__(self, data):# self.data = data# self.left = None# self.right = Nonedef level_order_traversal(root):# Replace this placeholder return statement with your codereturn ""
View the level order traversal of binary tree solution in C++, Java, JavaScript, and Ruby.
Initialize a queue, queue
, with the root node.
Iterate over the queue
until it’s empty:
For each level, calculate the size (level_size
) of the queue.
Initialize an empty list level_nodes
.
Iterate level_size
times:
Dequeue a node from the queue
.
Append the value of the dequeued node in level_nodes
.
Enqueue the left child and the right child of the dequeued node in queue
, if they exist.
Convert level_nodes
to a comma-separated string and store it in result
.
Return the result
.
Runtime complexity: Linear,
Memory complexity: Linear,
Given a Binary Tree, figure out whether it’s a Binary Search Tree. In a binary search tree, each node’s key value is smaller than the key value of all nodes in the right subtree, and is greater than the key values of all nodes in the left subtree. Below is an example of a binary tree that is a valid BST.
Below is an example of a binary tree that is not a BST.
# Definition for a binary tree node# class TreeNode:# def __init__(self, data):# self.data = data# self.left = None# self.right = Nonedef is_bst(root):# Replace this placeholder return statement with your codereturn False
View the binary tree solution in C++, Java, JavaScript, and Ruby.
We use a recursive method to explore the binary tree while keeping track of the allowed range for each node’s value. At the start, the root node’s value can be any number. As we go down the tree, we adjust this range. For a left child, the value must be smaller than its parent’s value. For a right child, the value must be larger than its parent’s value. By doing this, we make sure that each node’s value is within the acceptable range set by its parent. If we find any node with a value outside this range, it means the tree doesn’t follow the rules of a binary search tree and is not valid as a BST.
Define a recursive function is_bst_rec
that takes a node, min_val
, and max_val
as parameters.
Inside the function:
Check if the node is NULL. If so, return TRUE.
Verify if the current node’s value node.val
lies within the acceptable range defined by min_val
(exclusive) and max_val
(exclusive). If not, return FALSE.
Recursively call is_valid
for the left subtree with the updated max_val
as the current node’s value and for the right subtree with the updated min_val
as the current node’s value.
Initialize the range by calling is_bst_rec
with the root node, setting min_val
to negative infinity and max_val
to positive infinity.
Runtime complexity: Linear,
Memory complexity: Linear,
You are given a dictionary of words and a large input string. You have to find out whether the input string can be completely segmented into the words of a given dictionary. The following two examples elaborate on the problem further.
Given a dictionary of words.
Input string of “applepie” can be segmented into dictionary words.
Input string “applepeer” cannot be segmented into dictionary words.
def can_segment_string(s, dictionary):# Replace this placeholder return statement with your codereturn False
View the string segmentation solution in C++, Java, JavaScript, and Ruby.
The algorithm iteratively checks substrings of the input string against the dictionary. Starting with an empty string being segmentable, it gradually builds up segmentability information for larger substrings by considering all possible substrings and checking if they exist in the dictionary. Once we determine that a substring can be segmented into words from the dictionary, we mark the corresponding entry in an array dp
array as TRUE. Subsequently, when processing longer substrings, we use dp
to check whether shorter substrings within them have already been determined to be segmentable, avoiding recomputations.
Initialize a variable n
with the length of the input word and create a boolean array dp
with a size of n + 1
. Initialize all values of dp
to FALSE.
Set dp[0]
to TRUE to indicate that the empty string can be segmented.
Iterate over the indexes of the word from 1 to n
.
For each index i
, iterate over the indexes j
from 0 to i - 1
.
Check if dp[j]
is TRUE and if the substring word[j:i]
exists in the dictionary.
If both conditions are met, set dp[i]
to TRUE.
After completing the iterations, return dp[n]
to indicate whether the entire word can be segmented.
Runtime complexity: Polynomial,
Memory complexity: Linear,
Reverse the order of words in a given sentence (a string of words).
def reverse_words(sentence):# Replace this placeholder return statement with your codereturn ""
View the reverse words in a sentence solution in C++, Java, JavaScript, and Ruby.
The essence of this algorithm is to reverse the words in a string by a two-step method using two pointers, effectively manipulating the string in place. Initially, the entire string is reversed, which correctly repositions the words in reverse order. However, due to this reversal of the string, the corresponding characters of words are also reversed. To correct the order of the characters, iterate through the string, and identify the start and end of each word, considering space as a delimiter between words. Set two pointers at the start and end of the word to reverse the characters within that word. By repeating this process for each word, the method effectively achieves the goal without requiring additional memory.
Convert the string, sentence
, into a list of characters and store it back in sentence
.
Get the length of the sentence
and store it in str_len
.
Define a str_rev
function to reverse the entire list of characters sentence
.
Set the start pointer start
to 0 and iterate over the range from 0 to str_len + 1
using the pointer end
.
When encountering a space or reaching the end of a word, reverse the characters of the word using the str_rev
function with sentence
, start
, and end - 1
as arguments.
Update the start pointer start
to end + 1
.
Join the list of characters back into a string using join(sentence)
.
Return the reversed string.
Runtime complexity: Linear,
Memory complexity: Constant,
Suppose we have coin denominations of [1, 2, 5] and the total amount is 7. We can make changes in the following 6 ways:
def solve_coin_change(denominations, amount):# Replace this placeholder return statement with your codereturn -1
View the coin changing problem solution in C++, Java, JavaScript, and Ruby.
We can use dynamic programming to build simple coin change solutions, store them, and utilize them in larger coin change amounts. We start by considering the simplest case, which is making change for an amount of zero. At this stage, there’s only one way to make a change, which is by using no coins. Then, we proceed to compute the number of ways to make change for larger amounts by considering each coin denomination one by one. For each denomination, we iterate through the possible amounts starting from the denomination value up to the target amount. At each step, we update the count of ways to make change for the current amount by incorporating the count for smaller amounts. This iterative process ensures that we exhaustively explore all valid combinations of coins, leading to an accurate count of the total number of ways to make change for the target amount.
Initialize an array dp
of size (amount + 1)
to track the number of ways to make change.
Set dp[0] = 1
to denote the one way to make change for an amount of zero.
Iterate through each coin denomination:
For each denomination den
, iterate from den
to amount
.
Update dp[i]
by adding dp[i - den]
to account for the current denomination.
After processing all denominations and amounts, dp[amount]
holds the total number of ways to make change.
Runtime complexity: Quadratic,
Memory complexity: Linear,
Given a set of ‘n’ elements, find their Kth permutation. Consider the following set of elements:
All permutations of the above elements are (with ordering):
Here we need to find the Kth permutation.
def find_kth_permutation(v, k):# Replace this placeholder return statement with your codereturn ""
View the find Kth permutation solution in C++, Java, JavaScript, and Ruby.
We can divide all possible permutations into blocks, where each block represents a group of permutations sharing the same first element, and determine which block the target permutation belongs to by dividing the target position by the number of permutations within each block (block size).
Next, we can determine the first element of the permutation, based on the block’s position within all permutations and add it to our result. We also remove the selected element from the original set of elements to prevent duplication in the permutation.
Now, we can repeat the same process to find the remaining elements of the required permutation using the now reduced set of elements. We skipped the k
this way and repeat the same process on the updated, smaller block of numbers.
To implement the algorithm:
Start by defining a function called find_kth_permutation
. This takes three parameters: v
(the input list), k
(the target position), and result
(the result string).
Check if v
is empty. If so, return.
Calculate the number of permutations starting with the first digit of v
and store it in count
. This is done by computing the factorial of (n - 1)
using the function factorial
, where n
is the length of v
.
Determine the target block by dividing k-1
by the number of blocks count
and save in selected
.
Add the selected element v[selected]
to result
, and remove it from v
.
Update k
to reflect the remaining permutations by subtracting count
multiplied by selected
from k
.
Recursively call the function with the updated parameters until v
is empty or k
is 0.
Runtime complexity: Quadratic,
Memory complexity: Linear,
Answer any Amazon interview question by learning the patterns behind common questions.#
We are given a set of integers and we have to find all the possible subsets of this set of integers. The following example elaborates on this further.
Given set of integers:
All possile subsets for the given set of integers:
def get_all_subsets(nums):# Replace this placeholder return statement with your codereturn [[]]
View the find all subsets solution in C++, Java, JavaScript, and Ruby.
Instead of using nested loops to generate all possible combinations, this algorithm employs binary representation to efficiently derive all subsets of a given set. There are
To implement the algorithm:
Calculate the total number of subsets subsets_count
, which is
Iterate through numbers from 0
to subsets_count - 1
using a for
loop.
Inside the loop, create an empty set called st
to store the current subset.
Iterate through each index j
of the input list nums
using another for
loop.
Write and use the function get_bit
to determine whether the jth
bit of the current number i
is set to 1
or 0
.
If the jth
bit of i
is 1
, add the corresponding element from nums
to the current subset st
.
After iterating through all indexes of nums
, append the current subset st
to the list sets
.
Runtime complexity: Exponential,
Memory complexity: Exponential,
Print all braces combinations for a given value n so that they are balanced. For this solution, we will be using recursion.
def print_all_braces(n):# Replace this placeholder return statement with your codereturn []
View the print balanced brace combinations solution in C++, Java, JavaScript, and Ruby.
The algorithm has the following main points:
Adding parentheses:
Determine whether to add a left parenthesis “{” or a right parenthesis “}” based on certain conditions:
If the count of left parentheses (left_count
) is less than n
, a left parenthesis can be added to make a valid combination.
If the count of right parentheses (right_count
) is less than the count of left parentheses (left_count
), a right parenthesis can be added to make a valid combination.
Constructing the result: As the algorithm explores different combinations of parentheses, when left_count
and right_count
both reach n
, indicating that n
pairs of parentheses have been added and the combination is complete, it appends the current combination present in the output
list to the result
list.
Backtracking:
After exploring a possible path by adding a parenthesis and making recursive calls, the algorithm backtracks to explore other possible combinations.
Backtracking involves removing the last added parenthesis from the output
list using the pop
operation.
This reverts the output
list to its state before the current recursive call, allowing the algorithm to explore other possible combinations without missing any.
To implement the algorithm:
Initialize an empty list output
to store the current combination and an empty list result
to store all valid combinations.
Define a print_all_braces_rec
function with parameters n
(the total number of pairs), left_count
, right_count
, output
, and result
.
Inside print_all_braces_rec
:
Check if both left_count
and right_count
are greater than or equal to n
. If TRUE, append a copy of output
to result
.
If left_count
is less than n
, add a left parenthesis {
to output
, and recursively call print_all_braces_rec
with n
, left_count + 1
, right_count
, output
, and result
.
After the recursive call, remove (pop
) the last element from output
to backtrack.
If right_count
is less than left_count
, add a right parenthesis }
to output
, and recursively call print_all_braces_rec
with n
, left_count
, right_count + 1
, output
, and result
.
After the recursive call, remove (pop
) the last element from output
to backtrack.
Return the result
list containing all valid combinations of well-formed parentheses pairs for the given n
.
Runtime complexity: Exponential,
Memory complexity: Linear,
Given the root node of a directed graph, clone this graph by creating its deep copy so that the cloned graph has the same vertices and edges as the original graph.
Let’s look at the below graphs as an example. If the input graph is $G = (V, E)$ where V is set of vertices and E is set of edges, then the output graph (cloned graph) G’ = (V’, E’) such that V = V’ and E = E’. We are assuming that all vertices are reachable from the root vertex, i.e. we have a connected graph.
# Definition for a graph node# class Node:# def __init__(self, d):# self.data = d# self.neighbors = []def clone(root):# Replace this placeholder return statement with your codereturn root
View the clone a directed graph solution in C++, Java, JavaScript, and Ruby.
This algorithm clones a directed graph by traversing it recursively. It creates a new node for each encountered node, replicates its data, and traverses its neighbors. If a neighbor has already been cloned, it uses the existing clone; otherwise, it recursively clones the neighbor. This process continues until all nodes and their relationships are replicated, resulting in a deep copy of the original graph.
To implement the algorithm:
Initialize an empty dictionary called nodes_completed
to keep track of cloned nodes.
Write clone_rec
function, which takes root
node of the original graph and the nodes_completed
dictionary as parameters. Inside the function:
If the root node is None, return None.
Otherwise, create a new node p_new
with the same data as the root and add it to the nodes_completed
dictionary to mark it as visited.
Iterate through each neighbor p
of the root node:
If the neighbor p
has not been cloned yet (i.e., not present in nodes_completed
), recursively call clone_rec
with p
and nodes_completed
, and add the clone to the neighbors
list of p_new
.
If the neighbor p
has already been cloned, retrieve the clone x
from nodes_completed
and add it to the neighbors
list of p_new
.
Call clone_rec
and pass root
and nodes_completed
as parameters. When its recursion completes, return the cloned graph rooted at p_new
.
Runtime complexity: Linear,
Memory complexity: Linear,
Given a sorted array of integers, return the low and high index of the given key. You must return -1
if the indexes are not found. The array length can be in the millions with many duplicates.
In the following example, according to the key
, the low
and high
indices would be:
key
: 1, low
= 0 and high
= 0key
: 2, low
= 1 and high
= 1ke
y: 5, low
= 2 and high
= 9key
: 20, low
= 10 and high
= 10For the testing of your code, the input array will be:
1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 6, 6
def find_low_high_index(arr, key):# Replace this placeholder return statement with your codereturn []
View the find low/high index solution in C++, Java, JavaScript, and Ruby.
We can modify the binary search algorithm to find the first occurrence of the key by narrowing down the search range even if the value at the mid is equal to the key. The algorithm is designed to always move to the left whenever it encounters an element that’s equal to or greater than the target. So, when the search range is narrowed down to a single element, and that element matches the target, it means that this is the leftmost occurrence of the target element in the array. Similarly, to find the last occurrence of a key, we adjust the search range toward the right whenever the middle element is less than or equal to the key. This iterative process continues until the search range narrows down to a single element. At this point, either the last occurrence of the key is found, or it confirms the absence of the key in the array.
To implement the algorithm:
Initialize low
to 0, high
to the length of the array minus one, and mid
to high / 2
.
Enter a while
loop that continues while low
is less than or equal to high
.
Inside the loop, set mid_elem
to arr[mid]
Compare mid_elem
with the key:
If mid_elem
is less than the key, update low
to mid + 1
.
Otherwise, update high
to mid - 1
.
Update mid
to low + (high - low) / 2
.
After the loop, check if low
is within the bounds of the array and if the element at index low
equals the key. If so, return low
. Otherwise, return -1
.
Repeat the same steps to find the last occurrence, with the only difference being in the comparison step. If mid_elem
is less than or equal to the key, update low
to mid + 1
; and if it’s greater than the key, update high
to mid - 1
.
Runtime complexity: Logarithmic,
Memory complexity: Constant,
Search for a given number in a sorted array, with unique elements, that has been rotated by some arbitrary number. Return -1
if the number does not exist. Assume that the array does not contain duplicates.
def binary_search_rotated(arr, key):# Replace this placeholder return statement with your codereturn -1
View the search rotated array solution in C++, Java, JavaScript, and Ruby.
To tackle rotation in a binary search for a rotated array, we adjust the search range based on the middle element’s comparisons as follows:
If the element at the middle index matches the target, return the index of the middle element, as the target has been found.
If the element at the middle index is greater than or equal to the element at the start index, it implies that the left subarray (from the start to the middle) is sorted:
Now, check if the target falls within the range of elements from the leftmost element to the middle element. If it does, adjust the search range to focus on the left subarray.
Otherwise, adjust the search range to focus on the right subarray.
If the element at the middle index is less than the element at the end index, it indicates that the right subarray (from the middle to the end) is sorted:
Check if the target falls within the range of elements from the middle element to the rightmost element. If it does, adjust the search range to focus on the right subarray.
Otherwise, adjust the search range to focus on the left subarray.
To implement the algorithm:
Initialize low
to 0 and high
to the length of the array minus one.
Enter a while
loop that continues while low
is less than or equal to high
.
Inside the loop, calculate mid
using the formula low + (high - low) // 2
.
Check if the element at the middle index matches the target. If it does, return the index of the middle element.
If arr[low]
is less than or equal to arr[mid]
, it implies that the left subarray (from low to mid) is sorted:
If the target falls within this range, update the high
to mid - 1
. Otherwise, update the low
to mid + 1
.
Otherwise, it’s obvious that arr[low]
is greater than arr[mid]
, which it indicates that the right subarray (from mid to high) is sorted:
If the target
falls within this range, update the low
to mid + 1
. Otherwise, update the high
to mid - 1
.
If the target is not found after the loop, return -1
to indicate its absence in the array.
Runtime complexity: Logarithmic,
Memory complexity: Constant,
K
largest elements from an arrayT
, find the maximum path sum. The path may start and end at any node in the tree.push()
, min()
, and pop()
in $O(1)$ timek
places, which is non-negative.nth
number in the Fibonacci seriesTo land a software engineering job at Amazon, you need to know what lies ahead. The more prepared you are, the more confident you will be. So, let’s break it down.
Interview Timeline: The whole interview process takes 6 to 8 weeks to complete.
Types of Interviews: Amazon coding interviews consist of 5 to 7 interviews. This includes 1 assessment for fit and aptitude, 1-2 online tests, and 4-6 on-site interviews, also known as The Loop.
The Loop: The onsite interviews include 1 to 3 interviews with hiring managers and 1 bar raiser interview to evaluate Amazon’s 14 leadership principles.
Coding Questions: Amazon programming questions focus on algorithms, data structures, puzzles, and more.
Hiring Levels: Amazon usually hires at entry-level 4 (out of 12 total), and the average salary for that level ranges from $106,000 to $114,000 yearly.
Hiring Teams: Amazon hires based on teams. The most common hiring teams are Alexa and AWS.
Programming Languages: Amazon prefers the following programming languages for coding questions: Java, C++, Python, Ruby, and Perl.
Though coding interviews at Amazon are similar to other big tech companies, there are a few differences in their process, in particular, the Bar Raiser. Amazon brings in an objective third-party interviewer called a Bar Raiser, who evaluates candidates on Amazon’s 14 Leadership Principles. The Bar Raiser has complete veto power over whether or not you will be hired.
It is unlikely that you will know which of your interviewers is the Bar Raiser. The key is to take every part of the interview seriously and always assume you’re being evaluated for cultural fit as well as technical competency.
Below are the 14 values that you will be evaluated on.
Now that you have a sense of what to expect from an interview and know what kinds of questions to expect, let’s learn some preparation strategies based on Amazon’s unique interview process.
Make sure you’ve updated your resume and LinkedIn profile. Always use deliverables and metrics when you can as they are concrete examples of what you’ve accomplished. Typically recruiters will browse LinkedIn for candidates.
If an Amazon recruiter believes that you are a good match they will reach out to you (via email or LinkedIn) to set up a time to chat. Even if you don’t land the job this time, chatting with them is a great chance to network
It’s up to you to come to a coding interview fully prepared for technical assessment. I recommend at least three months of self-study to be successful. This includes choosing a programming language, reviewing the basics, and studying algorithms, data structures, system design, object-oriented programming, OS, and concurrency concepts.
You’ll also want to prepare for behavioral interviews.
It’s best to create a roadmap for your coding interview to keep yourself on track.
Expect a light 15-30 minute call where the recruiter will gauge your interest level and determine if you’re a good fit. The recruiter may touch on a few technical aspects. They just want to get an idea of your skills. Typical questions might include your past work experiences, your knowledge of the company/position, salary, and other logistical questions.
It’s important to have around 7-10 of your own questions ready to ask the interviewer. Asking questions at an early stage shows investment and interest in the role.
Once you complete the call with the recruiter, they’ll administer an online coding test, a debugging test, and an aptitude test. The debugging section will have around 6-7 questions, which you have 30 minutes to solve. The aptitude section will have around 14 multiple-choice questions, dealing with concepts like basic permutation combination and probabilities.
The coding test consists of two questions. You’ll have about 1.5 hours to complete it. Expect the test to be conducted through Codility, HackerRank, or another site. Expect some easy to medium questions that are typically algorithm related. Examples include:
Here are a few easily implemented tips to make the Amazon coding assessment more manageable for yourself:
Use your own preferred editor
Read each prompt multiple times through
Practice with less time than you’re given in the actual assessment
Once you’ve made it past the prescreen and online assessment, the recruiter will schedule your video/phone interviews, likely with a hiring manager or a manager from the team you’re looking to join. At this stage in the process, there will be one to three more interviews.
This is where they’ll ask you questions directly related to your resume, as well as data structures, algorithms, and other various coding questions that apply to the position. You can expect to write code, review code, and demonstrate your technical knowledge.
If you have successfully made it through the series of phone interviews, you’ll be invited for an on-site visit. This full day of on-site interviews is referred to as the “The Loop”. Throughout the day, you’ll meet with 4-6 people. Expect half of these interviews to be technical and the other half to assess soft skills. Be prepared to work through questions on a whiteboard and discuss your thought process.
Concepts that Amazon loves to test on are data structures algorithms. It’s important to know the runtimes, theoretical limitations, and basic implementation strategies of different classes of algorithms.
Data structures you should know: Arrays, Stacks, Queues, Linked lists, Trees, Graphs, Hash tables
Algorithms you should know: Breadth First Search, Depth First Search, Binary Search, Quicksort, Mergesort, Dynamic programming, Divide and Conquer
Generally, you’ll hear back from a recruiter within a week after your interviews. If you didn’t get an offer, Amazon will give you a call. You’ll likely have to wait another six months to re-apply. Judging that your on-site interviews went well, they’ll reach out to you, at which point they’ll make you an offer, send you documents to sign, and discuss any further questions you have.
Amazon is one of the top tech companies worldwide, and the company focuses on hiring only the best talent. Securing a software engineer position at Amazon is not a piece of cake, but with the right guidance, content, and practice questions, you can ace the Amazon software engineer interview.
Amazon software engineer interviews consist of four rounds. A major part of these four interview rounds involves technical interview questions. That is why having a solid grasp of computing fundamentals is crucial. Questions related to arrays, linked lists, strings, dynamic programming, and system design are common. We have mentioned some of the commonly asked questions above, so have a look and practice them thoroughly. For more interview prep questions, you can try Educative-99, created to help software engineers secure a job at top tech companies.
Besides technical questions, Amazon software engineer interviews also include behavioral questions. Amazon ensures that candidates are the right fit for the company culture, which is why you’ll be asked several behavioral questions as well. The cornerstone of Amazon’s culture is its 14 Leadership Principles. These principles, which include notions like “Customer Obsession,” “Ownership,” and “Bias for Action,” guide every decision the company makes, from high-level strategies to everyday interactions.
This dual approach to assessing candidates ensures that Amazon software engineers can solve complex technical challenges, collaborate effectively, handle difficult situations, and adapt to the company’s fast-paced environment. Hence, candidates must prepare themselves for both technical and behavioral challenges.
Cracking the Amazon coding interview comes down to the time you spend preparing, such as practicing coding questions, studying behavioral interview questions, and understanding Amazon’s company culture. There is no golden ticket, but more preparation will surely make you a more confident and desirable candidate.
To help you prepare for interviews, Educative has created several unique language-specific courses:
Available in multiple languages, these courses teach you the underlying patterns for any coding interview question.
This is coding interview prep reimagined, with your needs in mind.
Happy learning
Frequently Asked Questions
How do I clear my Amazon Coding interview test?
Is Amazon coding interview hard?
What are the different rounds of the Amazon interview?
Free Resources