While LeetCode can be a useful tool to prepare candidates for interview prep, more is needed to ace the interview. Candidates must focus on logical thinking, in-depth knowledge of foundational concepts, and communication skills.
CARVIEW |
Google coding interview questions: Top 18 questions explained
Landing a tech job at Google is no easy feat. Like other large tech giants, Google’s software engineering interview process is comprehensive and difficult, requiring weeks of preparation and practice. So, how can you prepare for the coding interview? What interview questions should prepare for?
Today we’ll revisit our Google Coding interview series and breakdown 18 common interview questions asked by Google recruiters.
Today we’ll cover the following:
Answer any interview problem by learning the patterns behind common questions.#
Google coding interview recap#
The entire Google interview process takes around two months to complete and consists of five interviews in total. For the interviews, there will primarily be three question formats: System Design Interview, general analysis, and technical skills.
Prescreen: The first interview consists of a prescreening with a Google employee, which will last 45 - 60 minutes. In this interview, the interviewer will test you on your knowledge of data structures and algorithms.
On-site interviews: If you past the prescreen, you will be invited to an on-site interview, in which you will be interviewed by 4-6 employees for 45 minutes each. These interviews will more rigorously test your knowledge of data structures and algorithms. Typically, you will be writing code on a Google Doc or whiteboard.
Decision: Based on your performance, you will be scored on a scale between 1 and 4, with 3 being the minimum threshold to hire.
Overall, Google wants to test your technical and logical application of computer science. Here are some popular topics to review before your interview:
- Algorithms
- Sorting
- Data structure
- Graphs
- Recursion
- Object-oriented programming
- Big-O notation
18 common Google coding interview questions#
1. Find the kth largest element in a number stream#
Design a class to efficiently find the kth largest element in a stream of numbers. The class should have the following two things:
-
The constructor of the class should accept an integer array (
nums
) containing initial numbers from the stream and an integer k. -
The class should expose a function
add(int value)
which will add the numbervalue
to the stream of numbers, and will return the kth largest number in the stream seen so far.
Examples#
Input: [4, 1, 3, 12, 7, 14], k = 3Calling add(6) should return '7'.Calling add(13) should return '12'.Calling add(4) should still return '12'.
Try it yourself#
Implement your solution in the following coding playground:
import heapqclass KthLargest:# Constructor to initialize heap and add values in itdef __init__(self, k, nums):pass# Adds element in the heap and return the Kth largestdef add(self, val):# Write your code herereturn -1
Time complexity of __init__
: O((n−k)logn)=O(nlogn)
Time complexity of add
: O(logk), where k represents the size of the heap.
Space complexity of __init__
: O(k)
Space complexity of add
: O(k), where k represents the size of the heap.
Solution summary#
The KthLargest
class is designed to efficiently maintain the k largest elements within a given list using a min heap. Here’s a breakdown of the solution:
-
Initialization: Upon initialization, the constructor sets the instance variables
k
andnums
, wherek
denotes the kth largest element to be tracked, andnums
represents the input list of numbers. The constructor heapifies the list and prunes elements from the heap until its length is reduced tok
, ensuring only the k largest elements remain. -
Adding elements: The
add
method is responsible for adding a new elementval
into the heap. If the length ofnums
exceedsk
after adding the new element, the methodheappop
is used to remove the smallest element from the heap. Subsequently, it returns the smallest element in the heap, which corresponds to the kth largest element.
2. Find k closest numbers#
You are given a sorted array of integers, nums
, and two integers, target
and k
. Your task is to return k
number of integers that are closest to the target value, target
. The integers in the output array should be in sorted order.
- An integer,
nums[i]
, is considered to be closer totarget
, as compared tonums[j]
when |nums[i]
-target
| < |nums[j]
-target
|. However, when |nums[i]
-target
| == |nums[j]
-target
|, the smaller of the two valuesnums[i]
andnums[j]
is selected.
Examples#
Input: nums = [1, 2, 3, 4, 5], k = 4, target = 3Output: [1, 2, 3, 4]Input: nums = [2, 4, 5, 6, 9], k = 3, target = 6Output: [4, 5, 6]
Try it yourself#
Implement your solution in the following coding playground:
def binary_search(array, target):left = 0right = len(array) - 1while left <= right:mid = (left + right) // 2if array[mid] == target:return midif array[mid] < target:left = mid + 1else:right = mid - 1return left
Time complexity: O(logn+k)
Space complexity: O(1)
Solution summary#
The find_closest_elements
function efficiently identifies the k closest elements to a given target value within a sorted list of numbers. Here’s an overview of the solution:
-
Edge cases handling: If the length of
nums
is equal to the value in the variablek
, the function returns the entire list since all elements are closest to the target. Similarly, if the target is less than or equal to the first element ofnums
, it returns the first k elements. Likewise, if the target is greater than or equal to the last element ofnums
, it returns the last k elements. -
Binary search for closest element: The function utilizes the
binary_search
function to find the index of the closest element to the target in the sorted listnums
. -
Window expansion: After finding the index of the closest element, the function expands a window around this element to capture the k closest elements. It iteratively adjusts the window boundaries based on the proximity of elements to the target until the window contains exactly k elements.
-
Result extraction: Finally, the function returns the elements within the determined window, representing the k closest numbers to the target.
3. Sum of two values#
Given an array of integers arr
and a target value t
, find two array indices such that the values stored at those positions add up to t
. Return these indices, ensuring each index is used only once. There is exactly one valid solution.
Note: We will assume that the array is zero-indexed and the output order doesn’t matter.
Examples#
Given arr = [2, 7, 11, 15], t = 9return [0, 1] because arr[0] + arr[1] = 2 + 7 = 9Given arr = [3, 2, 4], t = 6return [1, 2] because arr[1] + arr[2] = 2 + 4 = 6
Try it yourself#
Implement your solution in the following coding playground:
def two_sum(arr, t):# Write your code herereturn []
Time complexity: O(n)
Note: In the worst case, the time complexity for operations like accessing and inserting elements in a dictionary can degrade to O(n), which could potentially result in an overall time complexity of O(n2) if there are many collisions. However, such scenarios are uncommon.
Space complexity: O(n)
Solution summary#
The two_sum
function efficiently finds the indices of two numbers in an array that sum up to a given target value t
. Here’s an overview of the solution:
-
Dictionary for indices storage: The function initializes an empty dictionary
indices
to store the indices of array elements. -
Iterative search for complement: It iterates through the array using the
enumerate
function to access both the elements and their indices. For each elementnum
, it calculates its complement (t - num
) and checks if this complement exists in theindices
dictionary. -
Return indices if complement found: If the complement is found in the dictionary, it means that the current element, along with its complement, sums up to the target value. The function returns the indices of these two elements.
-
Update indices dictionary: If the complement is not found, the current element’s index is stored in the indices dictionary with the element itself as the key.
-
Handling no solution: If no solution is found after iterating through the entire array, the function returns an empty list.
4. Delete node with a given key#
We are given the head of a linked list and a key. We have to delete the node that contains this given key.
Examples#
Input: head = [A, B, C, D], key = AOutput: [B, C, D]Input: head = [B, C, D], key = COutput: [B, D]Input: head = [B, D], key = DOutput: [B]
Try it yourself#
Implement your solution in the following coding playground:
from linked_list import ListNodedef delete_node(head, key):# Write your code herereturn headif __name__ == "__main__":# Test the LinkedListlinked_list = ListNode()linked_list.append('A')linked_list.append('B')linked_list.append('C')linked_list.append('D')linked_list.print_list()print("Deleting the A node")linked_list = delete_node(linked_list, 'A')linked_list.print_list()print("Deleting the C node")linked_list = delete_node(linked_list, 'C')linked_list.print_list()print("Deleting the D node")linked_list = delete_node(linked_list, 'D')linked_list.print_list()
Time complexity: O(n)
Space complexity: O(1)
Solution summary#
The delete_node
function aims to remove a node with a specified key from a singly linked list. Here’s a summary of the solution:
-
Check if the head node contains the value passed in the variable
key
. -
Otherwise, traverse the linked list using the pointers
prev_node
andcurr_node
to point to two consecutive nodes. -
While traversing, compare the value of each node with the given key. If a node with the key value is found, update the
next
pointer of the previous node (prev_node
) to bypass the current node (curr_node
), effectively removingcurr_node
from the linked list. -
Return the head of the modified linked list.
5. Copy linked list with arbitrary pointer#
You are given a linked list where each 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 (inserting, modifying and removing) should not affect the copied list.
Example#
Input: [[7, null], [13, 0], [11, 4], [10, 2], [1, 0]]Output: [[7, null], [13,0], [11, 4], [10, 2], [1, 0]]
Try it yourself#
Implement your solution in the following coding playground:
class LinkedListNode:def __init__(self, data):self.data = dataself.next = Noneself.arbitrary = Nonedef deep_copy_with_arbitrary_pointer(head):# Write your code herereturn Nonedef print_linked_list(head):current = headwhile current:arbitrary_data = current.arbitrary.data if current.arbitrary else Noneprint(f"Data: {current.data}, Arbitrary data: {arbitrary_data}, Memory address: {id(current)}")current = current.next# Example usage with provided input and outputif __name__ == "__main__":# Create the original linked list with arbitrary pointershead = LinkedListNode(7)node1 = LinkedListNode(13)node2 = LinkedListNode(11)node3 = LinkedListNode(10)node4 = LinkedListNode(1)head.next = node1node1.next = node2node2.next = node3node3.next = node4head.arbitrary = Nonenode1.arbitrary = headnode2.arbitrary = node4node3.arbitrary = node2node4.arbitrary = headprint("Original linked list: ")print_linked_list(head)# Perform deep copy with arbitrary pointercopied_head = deep_copy_with_arbitrary_pointer(head)print("\nCopied linked list for verification: ")print_linked_list(copied_head)
Time complexity: O(n)
Space complexity: O(n)
Solution summary#
To perform a deep copy of a linked list with arbitrary pointers, the algorithm follows these steps:
- Create a mapping: Iterates through the original linked list (
head
) and creates a mapping (mapping
) between each original node and its corresponding new node. Each new node is initialized with the same data as the original node. - Set pointers: While iterating through the original list again, it updates the next pointers of the new nodes based on the mapping of original next pointers. Simultaneously, it sets the arbitrary pointers of the new nodes using the mapping to find corresponding new nodes for the arbitrary pointers of the original nodes.
- Return the head of the new list: Finally, it returns the head of the new list (
mapping[head]
), which now represents a deep copy of the original linked list with correct next and arbitrary pointers.
6. Mirror binary tree nodes#
Given the root node of a binary tree, swap the ‘left’ and ‘right’ children for each node. The example below shows how the mirrored binary tree would look like for a given binary tree.
Example#
Try it yourself#
Implement your solution in the following coding playground:
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightdef print_tree(self, level=0):if self.right:self.right.print_tree(level + 1)print(' ' * 6 * level+ ' ', self.val)if self.left:self.left.print_tree(level + 1)def mirror_tree(root):# Write your code herereturn root# Test the function with the provided exampleif __name__ == "__main__":# Define the tree according to the provided exampleroot = TreeNode(40)root.left = TreeNode(100)root.right = TreeNode(400)root.left.left = TreeNode(150)root.left.right = TreeNode(50)root.right.left = TreeNode(300)root.right.right = TreeNode(600)# Print the original treeprint("Original Tree:")root.print_tree()# Perform mirror operationmirrored_root = mirror_tree(root)# Print the mirrored treeprint("\nMirrored Tree:")mirrored_root.print_tree()
Time complexity: O(n)
Space complexity: O(n)
Solution summary#
The algorithm for mirroring binary tree nodes follows these steps:
-
Base case: If the
root
isNone
, returnNone
. -
Otherwise, swap the left and right children of the current
root
node to perform a single mirroring step. Then call themirror_tre
e function on the left and right child nodes to mirror the two subtrees recursively. -
Return the
root
node.
7. Check if two binary trees are identical#
Given the roots of two binary trees, determine if these trees are identical or not. Identical trees have the same structure and data at each node.
Examples#
Input: 1 1/ \ / \2 3 2 3Output: TrueInput: 1 1/ \ / \2 1 1 2Output: False
Try it yourself#
Implement your solution in the following coding playground:
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightdef print_tree(self):self._print_tree(self, 0)def _print_tree(self, root, depth):if root:self._print_tree(root.right, depth + 1)print(" " * depth + str(root.val))self._print_tree(root.left, depth + 1)def identical(root1, root2):# Write your code herereturn Noneif __name__ == "__main__":# Example 1root1 = TreeNode(1)root1.left = TreeNode(2)root1.right = TreeNode(3)root2 = TreeNode(1)root2.left = TreeNode(2)root2.right = TreeNode(3)print("Tree 1:")root1.print_tree()print("Tree 2:")root2.print_tree()print("Are trees identical?", identical(root1, root2)) # Output: True# Example 2root1 = TreeNode(1)root1.left = TreeNode(2)root1.right = TreeNode(1)root2 = TreeNode(1)root2.left = TreeNode(1)root2.right = TreeNode(2)print("\nTree 1:")root1.print_tree()print("Tree 2:")root2.print_tree()print("Are trees identical?", identical(root1, root2)) # Output: False
If both roots contain identical values and their left and right subtrees are also identical, return True
. Otherwise return False
.
Time complexity: O(n)
Space complexity: O(n)
Solution summary#
The identical
function checks if two binary trees are identical. It follows these steps:
- Base case: If both roots are
None
, they are identical, so returnTrue
. - Recursive case:
- If one of the roots is
None
while the other is not, they are not identical, so returnFalse
. - If both roots contain identical values and their left and right subtrees are also identical, return
True
. Otherwise returnFalse
.
- If one of the roots is
8. String segmentation#
You are given a list of words, word_dict
, and a large input string, s
. You have to find out whether the input string can be completely segmented into the words of the given word_dict
. Note that you are allowed to reuse the same word multiple times from word_dict
.
Return True
if s
can be segmented otherwise, return False
.
The following example elaborates the problem further.
Example#
Input: s = "applepenapple", word_dict = ["apple", "pen"]Output: TrueExplanation: Return True because "applepenapple" can be segmented as "apple pen apple".
Try it yourself#
Implement your solution in the following coding playground:
def can_segment_string(s, word_dict):# Write your code herereturn False
Time complexity: O(n2m) (where m is the size of the dictionary)
Space complexity: O(n)
Solution summary#
The can_segment_string
function efficiently checks if a string can be segmented into words from a given list using dynamic programming. Here’s how it works:
-
Initialization: Determine the length of the input string
s
(n = len(s)
). Initialize a boolean arraydp
(dp = [False] * (n + 1)
) wheredp[i]
indicates if the substrings[0:i]
can be segmented into words fromword_dict
. Setdp[0] = True
to indicate that an empty string can always be segmented. -
Dynamic programming approach: Iterate over each value of
i
from 0 ton
representing the length of the string being considered for segmentation. Iterate overj
from 0 toi-1
, representing potential end points of the first segment. Check ifdp[j]
isTrue
(indicatings[0:j]
can be segmented) ands[j:i]
exists inword_dict
. If true, setdp[i] = True
. Break out of the inner loop once a segmentation is found to avoid unnecessary checks. -
Result: Return
dp[n]
, wheredp[n]
isTrue
if the entire strings
can be segmented into words fromword_dict
, otherwiseFalse
.
9. Find all palindrome substrings#
Given a string find all non-single letter substrings that are palindromes.
Example#
Input: "abacddcbe"Output: ["aba", "cddc", "dd"]Explanation: There are three palindromic strings: "aba", "cddc", and "dd".
Try it yourself#
Implement your solution in the following coding playground:
def find_palindrome_substrings(s):# Write your code herereturn None
Time complexity: O(n3)
Space complexity: O(n3)
Solution summary#
Let’s break down the solution step by step:
-
Iterating through substrings: The code starts by iterating through all possible substrings of the input string
s
. It does this with two nested loops:- The outer loop iterates over each character of the string
s
, denoted by the variablei
. - The inner loop iterates over each character starting from
i+2
to the end of the strings
, denoted by the variablej
. This ensures that the length of the substring being considered is greater than 1, indicating a non-trivial palindrome.
- The outer loop iterates over each character of the string
-
Extracting substrings: For each combination of
i
andj
, the code extracts the substring using slicing:substring = s[i:j]
. -
Checking for palindromes: For each extracted substring, the code checks if it’s a palindrome:
- It does this by comparing the substring with its reverse using
substring == substring[::-1]
.
- It does this by comparing the substring with its reverse using
-
Storing palindromes: If the substring is a palindrome, it’s appended to the list
palindromes
. -
Returning palindromes: Finally, the list of palindromes is returned.
Answer any interview problem by learning the patterns behind common questions.#
10. Largest sum subarray#
Given an array of integers, find the contiguous subarray (containing at least one element) that has the largest sum and return the sum.
Example#
Input: [-2,1,-3,4,-1,2,1,-5,4],Output: 6Explanation: [4,-1,2,1] has the largest sum = 6.
Try it yourself#
Implement your solution in the following coding playground:
def max_subarray_sum(nums):# Write your code herereturn
Time complexity: O(n)
Space complexity: O(1)
Solution summary#
To determine the largest sum subarray in an array, the algorithm follows these steps:
- Initialize
max_sum
andcurrent_sum
variables to the first element of the array. - Iterate through each element in the input array. At each iteration, update
current_sum
by taking the maximum between the current element and the sum of the current element and the previouscurrent_sum
. - Update
max_sum
by taking the maximum between the currentmax_sum
and the updatedcurrent_sum
. - Finally, return
max_sum
as the result.
11. Determine if the number is valid#
Given an input string, determine if it represents a valid number or not. For this problem, we assume that a valid number consists of numeric digits (in the range 0–9), an optional sign (+ or -) at the beginning, and an optional decimal point that must be followed by one or more digits.
Examples#
4.325 is a valid number.1.1.1 is NOT a valid number.222 is a valid number.22. is NOT a valid number.0.1 is a valid number.22.22. is NOT a valid number.
Try it yourself#
Implement your solution in the following coding playground:
class STATE:START, INTEGER, DECIMAL, UNKNOWN, AFTER_DECIMAL = range(5)def get_next_state(current_state, ch):# Write your code herepassdef is_number_valid(s):# Write your code herereturn None
Time complexity: O(n)
Space complexity: O(1)
Solution summary#
To determine if a given string represents a valid number, the algorithm follows these steps:
-
If the input string is empty, return
False
. -
Initialize an index variable
i
to track the position in the string. -
If the first character is a positive or negative sign (’+’ or ‘-’), increment
i
to skip it. -
Define a state variable
current_state
to track the state of parsing the string. -
Iterate through the characters of the string using index
i
. -
Update
current_state
based on the current character using theget_next_state
function. -
If
current_state
becomes UNKNOWN at any point, returnFalse
, indicating an invalid number. -
If the final state is DECIMAL, return
False
, as the number cannot end with a decimal point. -
If all characters are successfully parsed and no invalid states are encountered, return
True
, indicating a valid number.
12. Print balanced brace combinations#
Given a positive integer n, print all strings containing n pairs of braces that are balanced.
Example#
For example, given n = 3, a solution set is:["{{{}}}","{{}{}}","{{}}{}","{}{{}}","{}{}{}"]
Try it yourself#
Implement your solution in the following coding playground:
def generateBraces(n):# Write your code herereturn []
Time complexity: O(22n)
Space complexity: O(n)
Solution summary#
To generate all balanced brace combinations for a given value in variable n
, the algorithm follows these steps:
-
Start with an empty result list.
-
Initialize a stack with a tuple containing a string
'{'
, and countersleft
andright
, both initially set to1
and0
respectively. -
While the stack is not empty:
- Pop the top tuple from the stack, and store its three elements in the variables
s
,left
, andright
. - If the length of
s
reaches2*n
andleft
equalsright
, appends
to the result list. - Otherwise, if
left
is less thann
, push a tuple onto the stack withs
appended with a left brace, and theleft
variable incremented by 1. - Also, if
right
is less thanleft
, push a tuple onto the stack withs
appended with a right brace, andright
incremented by 1.
- Pop the top tuple from the stack, and store its three elements in the variables
-
Return the result list containing all balanced brace combinations.
# Initialize the cache with capacity 2cache = LRUCache(2)cache.put(1, 1)cache.put(2, 2)print(cache.get(1)) # returns 1cache.put(3, 3) # evicts key 2print(cache.get(2)) # returns -1 (not found)cache.put(4, 4) # evicts key 1print(cache.get(1)) # returns -1 (not found)print(cache.get(3)) # returns 3print(cache.get(4)) # returns 4LRUCache cache = new LRUCache( 2 /* capacity */ );cache.put(1, 1);cache.put(2, 2);cache.get(1); # returns 1cache.put(3, 3); # evicts key 2cache.get(2); # returns -1 (not found)cache.put(4, 4); # evicts key 1cache.get(1); # returns -1 (not found)cache.get(3); # returns 3cache.get(4); # returns 4
Try it yourself#
Implement your solution in the following coding playground:
class ListNode:def __init__(self, key=None, value=None):self.key = keyself.value = valueself.prev = Noneself.next = Noneclass LRUCache:def __init__(self, capacity):self.capacity = capacityself.cache = {}self.head = ListNode()self.tail = ListNode()self.head.next = self.tailself.tail.prev = self.headdef _add_node(self, node):node.prev = self.headnode.next = self.head.nextself.head.next.prev = nodeself.head.next = nodedef _remove_node(self, node):prev = node.prevnext_node = node.nextprev.next = next_nodenext_node.prev = prevdef _move_to_head(self, node):self._remove_node(node)self._add_node(node)def get(self, key):# Write your code herereturn -1def set(self, key, value):# Write your code herepass
Time complexity: O(1)
Space complexity: O(1)
Solution summary#
To implement the LRU cache, we utilize a combination of a doubly linked list and a dictionary:
-
We define a
ListNode
class to represent individual nodes in a doubly linked list. Each node contains key-value pairs and pointers to the previous and next nodes. -
We implement the
LRUCache
class, which is initialized with a given capacity. It also maintains a dictionary (cache
) to store key-node mappings and doubly linked list nodes to maintain the order of recently used items. -
The
_add_node
method adds a node to the head of the linked list. -
The
_remove_node
method removes a given node from the linked list. -
The
_move_to_head
method moves a given node to the head of the linked list, indicating it as the most recently used. -
The
get
method retrieves the value associated with a given key. If the key exists in the cache, it moves the corresponding node to the head of the linked list and returns the value stored in it; otherwise, it returns-1
. -
The
set
method sets the value associated with a given key. If the key exists in the cache, it updates the value and moves the corresponding node to the head of the linked list. If the key does not exist in the cache and the cache is at full capacity, it removes the least recently used node from the tail of the linked list and thecache
dictionary. Then, it adds a new node with the given key and value to the head of the linked list and the cache dictionary.
14. Find Low/High Index#
Given an array of integers arr
sorted in ascending order, find the starting and ending position of a given target value (key
). If the target is not found in the array, return [-1, -1]
.
Example#
Input: arr = [5, 7, 7, 8, 8, 10], key = 8Output: [3, 4]
Try it yourself#
Implement your solution in the following coding playground:
def find_low_index(arr, key):# Write your code herereturn -1def find_high_index(arr, key):# Write your code herereturn -1def find_low_high_index(arr, key):# Write your code herereturn []
Time complexity: O(logn)
Space complexity: O(1)
Solution summary#
The solution consists of two primary functions: find_low_index
and find_high_index
. These functions utilize a binary search algorithm to determine the starting and ending positions of a given target value in a sorted array.
-
find_low_index(arr, key)
: This function identifies the lowest index of the target valuekey
in the sorted arrayarr
. It employs binary search to locate the leftmost occurrence of the target value. -
find_high_index(arr, key)
: Similar tofind_low_index
, this function determines the highest index of the target valuekey
in the sorted arrayarr
. It utilizes binary search to find the rightmost occurrence of the target value. -
find_low_high_index(arr, key)
: This function integrates the results fromfind_low_index
andfind_high_index
to return a list containing the low and high indices of the target valuekey
in the sorted arrayarr
.
15. Merge overlapping intervals#
Given a collection of intervals, merge all overlapping intervals.
Examples#
Example 1:Input: [[1,3],[2,6],[8,10],[15,18]]Output: [[1,6],[8,10],[15,18]]Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].Example 2:Input: [[1,4],[4,5]]Output: [[1,5]]Explanation: Intervals [1,4] and [4,5] are considered overlapping.
Try it yourself#
Implement your solution in the following coding playground:
def merge_intervals(intervals):# Write your code herereturn []
Time complexity: O(nlogn)
Space complexity: O(n)
Solution summary#
To merge overlapping intervals, the algorithm follows these steps:
-
Sort the intervals based on their start time.
-
Initialize an empty list to store merged intervals and add the first interval from the sorted list.
-
Iterate through the sorted intervals starting from the second interval.
-
If the current interval overlaps with the previous one, merge them by updating the end time of the previous interval to the maximum of both end times.
-
If there’s no overlap, add the current interval to the merged list.
-
Return the merged list of intervals as the result.
16. Path sum#
Given a binary tree and a target, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given target.
Example#
Given the binary tree below and target = 22,5/ \4 8/ / \11 13 4/ \ \7 2 1return True, as there exist a root-to-leaf path 5->4->11->2 whose path sum is 22.
Try it yourself#
Implement your solution in the following coding playground:
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightdef print_tree(self, level=0):if self.right:self.right.print_tree(level + 1)print(" " * level + str(self.val))if self.left:self.left.print_tree(level + 1)def has_path_sum(root, target):# Write your code herereturn Noneif __name__ == "__main__":# Create the binary treeroot = TreeNode(5)root.left = TreeNode(4)root.right = TreeNode(8)root.left.left = TreeNode(11)root.left.left.left = TreeNode(7)root.left.left.right = TreeNode(2)root.right.left = TreeNode(13)root.right.right = TreeNode(4)root.right.right.right = TreeNode(1)target = 22# Check if there exists a root-to-leaf path with the given sumresult = has_path_sum(root, target)# Output the resultprint("Does there exist a root-to-leaf path with sum {}?".format(target))print("Result:", result)root.print_tree()
Time complexity: O(n)
Space complexity: O(n)
Solution summary#
To determine if there exists a path from the root to a leaf node in a binary tree such that the sum of values along the path equals a given target, the algorithm follows these steps:
-
Define a recursive function
has_path_sum(root, target_sum)
to traverse the binary tree. -
If the
root
isNone
there is no path that sums to the given target. So, returnFalse
. -
If the
root
is a leaf node (i.e., it has no left or right child) and if its value equals the target sum, returnTrue
. -
Recursively call the function on the left and right subtrees, subtracting the value of the current node from the target.
-
If either recursive call returns
True
, indicating a path with the target exists in either subtree, returnTrue
. -
If neither subtree contains a path with the target, return
False
.
17. Find the missing number#
We are given an array containing n distinct numbers taken from the range 0 to n. Since the array has only n numbers out of the total n+1 numbers, find the missing number.
Examples#
Example 1:Input: [4, 0, 3, 1]Output: 2Example 2:Input: [8, 3, 5, 2, 4, 6, 0, 1]Output: 7
Try it yourself#
Implement your solution in the following coding playground:
def find_missing_number(nums):# Write your code herereturn -1
Time complexity: O(n)
Space complexity: O(1)
Solution summary#
To find the missing number in an array, the algorithm follows these steps:
-
Calculate the expected sum of the numbers from 0 to n using the formula 2n×(n+1) , where n is the length of the input array.
-
Calculate the actual sum of the numbers in the input array using the
sum
function. -
The missing number is the difference between the expected sum and the actual sum.
-
Return the missing number as the result.
18. Reverse a linked list#
Reverse a singly linked list. See the example below.
Example#
Input: 1 -> 2 -> 3 -> 4 -> 5 -> NULLOutput: 5 -> 4 -> 3 -> 2 -> 1 -> NULL
Try it yourself#
Implement your solution in the following coding playground:
from linked_list import ListNode, create_linked_list, print_linked_listdef reverse_linked_list(head):# Write your code herereturn headif __name__ == "__main__":# Create a linked listvalues = [1, 2, 3, 4, 5]head = create_linked_list(values)# Print the original linked listprint("Original linked list:")print_linked_list(head)# Reverse the linked listreversed_head = reverse_linked_list(head)# Print the reversed linked listprint("\nReversed linked list:")print_linked_list(reversed_head)
Time complexity: O(n)
Space complexity: O(1)
Solution summary#
To reverse a linked list, the algorithm follows these steps:
- Initialize two pointers,
prev
andcurrent
, toNone
and the head of the linked list, respectively. - Iterate through the linked list, and at each step, store the next node of the current node in a temporary variable
next_node
. - Update the
next
pointer of the current node to point to the previous node (prev
). - To prepare for the next round, update
prev
to be the current node andcurrent
to be the node stored in the temporary variablenext_node
. - After the last iteration, the
prev
pointer points to the new head of the reversed linked list. - Return the head of the reversed linked list (
prev
).
More practice and preparation#
Congratulations on finishing these problems! To pass the coding interview, it’s important that you continue practice. To prepare for the Google Coding Interview, you should brush up on dynamic programming, backtracking, recursion, greedy algorithms, and divide & conquer. Here are some more common coding interview questions to practice.
-
Determine if the sum of three integers is equal to the given value
-
Intersection point of two linked lists
-
Move zeros to the left
-
Add two integers
-
Merge two sorted linked lists
-
Convert binary tree to doubly linked list
-
Level order traversal of binary tree
-
Reverse words in a sentence
-
Find maximum single sell profit
-
Calculate the power of a number
-
Find all possible subsets
-
Clone a directed graph
-
Serialize / deserialize binary tree
-
Search rotated array
-
Set columns and rows as zeros
-
Connect all siblings
-
Find all sum combinations
-
Clone a directed graph
-
Closest meeting point
-
Search for the given key in a 2D matrix
Wrapping up and resources#
Cracking the Google coding interview simply comes down to repetition and practice. It’s important that you continue practice more interview questions after reading this article.
To help you prepare for Google interviews, Educative has created these unique language-specific courses:
Available in multiple languages, these courses will give you a hands-on mastery of the underlying patterns to help you solve any coding interview problem.
This is coding interview prep reimagined, with your needs in mind.
Happy learning!
Continue reading about coding interview prep#
Frequently Asked Questions
Is LeetCode enough for Google?
Is LeetCode enough for Google?
Free Resources