Solve classic array, graph, and parsing problems
Company: Tesla
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: HR Screen
Quick Answer: This multi-part question evaluates algorithmic problem-solving and data-structure competency, covering expression parsing, array and sequence manipulation, sorting-based counting, interval merging, graph traversal/union-find, in-place rearrangement, hashing for set operations, and DAG/topological ordering within the Coding & Algorithms domain.
Part 1: Expression Evaluator
Constraints
- 1 <= len(s) <= 10^5
- s contains digits, spaces, and the operators +, -, *, /
- The expression is valid and all intermediate results fit in a signed 32-bit integer
Examples
Input: ('3+2*2',)
Expected Output: 7
Explanation: Multiplication happens before addition: 3 + (2 * 2) = 7.
Input: (' 3/2 ',)
Expected Output: 1
Explanation: 3 / 2 truncates toward zero, so the result is 1.
Input: (' 3+5 / 2 ',)
Expected Output: 5
Explanation: 5 / 2 = 2 after truncation, so 3 + 2 = 5.
Input: ('42',)
Expected Output: 42
Explanation: A single number evaluates to itself.
Input: ('14-3/2',)
Expected Output: 13
Explanation: 3 / 2 = 1 after truncation, so 14 - 1 = 13.
Input: ('0-3/2',)
Expected Output: -1
Explanation: 3 / 2 = 1 after truncation, so 0 - 1 = -1.
Solution
def solution(s):
total = 0
last = 0
num = 0
op = '+'
n = len(s)
for i, ch in enumerate(s):
if ch.isdigit():
num = num * 10 + int(ch)
if (not ch.isdigit() and ch != ' ') or i == n - 1:
if op == '+':
total += last
last = num
elif op == '-':
total += last
last = -num
elif op == '*':
last = last * num
elif op == '/':
if last < 0:
last = -((-last) // num)
else:
last = last // num
op = ch
num = 0
return total + lastTime complexity: O(n). Space complexity: O(1).
Hints
- When you reach a new operator, you have finished reading the previous number.
- Keep a running total for completed + and - terms, and a separate last term for * and / precedence.
Part 2: Count Triangle Triplets
Constraints
- 0 <= len(nums) <= 2000
- 1 <= nums[i] <= 10^4
- An O(n^2) solution after sorting is expected
Examples
Input: ([2, 2, 3, 4],)
Expected Output: 3
Explanation: The valid index triplets are (0,1,2), (0,2,3), and (1,2,3).
Input: ([4, 2, 3, 4],)
Expected Output: 4
Explanation: After sorting to [2,3,4,4], the four valid triplets are formed by indices (0,1,2), (0,1,3), (0,2,3), and (1,2,3).
Input: ([1, 1, 3, 4],)
Expected Output: 0
Explanation: No three sides satisfy the triangle inequality.
Input: ([],)
Expected Output: 0
Explanation: An empty list cannot contain any triplet.
Input: ([5, 5, 5, 5],)
Expected Output: 4
Explanation: Every choice of 3 indices works, so the count is C(4,3) = 4.
Solution
def solution(nums):
nums = sorted(nums)
n = len(nums)
count = 0
for k in range(n - 1, 1, -1):
left = 0
right = k - 1
while left < right:
if nums[left] + nums[right] > nums[k]:
count += right - left
right -= 1
else:
left += 1
return countTime complexity: O(n^2). Space complexity: O(n).
Hints
- After sorting, if nums[i] + nums[j] > nums[k], then every index from i to j - 1 paired with j will also work for that k.
- Try fixing the largest side first and use two pointers for the other two sides.
Part 3: Merge Ranges
Constraints
- 0 <= len(intervals) <= 10^5
- -10^9 <= l <= r <= 10^9
- Intervals are closed and merging should include endpoint touching
Examples
Input: ([[1,3],[2,6],[8,10],[15,18]],)
Expected Output: [[1,6],[8,10],[15,18]]
Explanation: [1,3] and [2,6] overlap, so they merge into [1,6]. The other intervals do not connect.
Input: ([[1,4],[4,5]],)
Expected Output: [[1,5]]
Explanation: These intervals touch at endpoint 4, so they must be merged.
Input: ([],)
Expected Output: []
Explanation: An empty input has no intervals to merge.
Input: ([[5,7],[1,4],[2,3],[7,8]],)
Expected Output: [[1,4],[5,8]]
Explanation: After sorting, [1,4] absorbs [2,3], and [5,7] merges with [7,8] because they touch at 7.
Input: ([[-5,-3],[-3,0],[2,2]],)
Expected Output: [[-5,0],[2,2]]
Explanation: The first two intervals touch at -3, so they merge into [-5,0]. [2,2] remains separate.
Solution
def solution(intervals):
if not intervals:
return []
intervals = sorted(intervals, key=lambda interval: (interval[0], interval[1]))
merged = [intervals[0][:]]
for start, end in intervals[1:]:
last = merged[-1]
if start <= last[1]:
if end > last[1]:
last[1] = end
else:
merged.append([start, end])
return mergedTime complexity: O(n log n). Space complexity: O(n).
Hints
- Sorting by interval start lets you process intervals from left to right.
- Compare each interval only with the last merged interval.
Part 4: Count Land Clusters
Constraints
- 0 <= m, n <= 300
- grid[r][c] is either '1' or '0'
- An O(m * n) traversal is expected
Examples
Input: ([['1','1','0','0','0'],['1','1','0','0','0'],['0','0','1','0','0'],['0','0','0','1','1']],)
Expected Output: 3
Explanation: There are three separate groups of land: the top-left block, the single land cell in the middle, and the bottom-right pair.
Input: ([],)
Expected Output: 0
Explanation: An empty grid contains no land clusters.
Input: ([['1','0','1'],['0','1','0'],['1','0','1']],)
Expected Output: 5
Explanation: Diagonal cells do not connect, so each '1' is its own cluster.
Input: ([['0']],)
Expected Output: 0
Explanation: A single water cell contains no land.
Input: ([['1','1','1'],['0','1','0'],['1','1','1']],)
Expected Output: 1
Explanation: All land cells are connected through the center, forming one cluster.
Input: ([['1']],)
Expected Output: 1
Explanation: A single land cell is one cluster.
Solution
def solution(grid):
if not grid or not grid[0]:
return 0
rows, cols = len(grid), len(grid[0])
visited = [[False] * cols for _ in range(rows)]
clusters = 0
directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
for r in range(rows):
for c in range(cols):
if grid[r][c] == '1' and not visited[r][c]:
clusters += 1
stack = [(r, c)]
visited[r][c] = True
while stack:
cr, cc = stack.pop()
for dr, dc in directions:
nr, nc = cr + dr, cc + dc
if 0 <= nr < rows and 0 <= nc < cols:
if grid[nr][nc] == '1' and not visited[nr][nc]:
visited[nr][nc] = True
stack.append((nr, nc))
return clustersTime complexity: O(m * n). Space complexity: O(m * n).
Hints
- Every time you find an unvisited land cell, start a DFS or BFS and mark the whole component.
- Be careful not to count the same land cell more than once.
Part 5: Smallest Missing Positive
Constraints
- 0 <= len(nums) <= 10^5
- -10^9 <= nums[i] <= 10^9
- A linear-time, constant-extra-space solution is expected
Examples
Input: ([1, 2, 0],)
Expected Output: 3
Explanation: The values 1 and 2 are present, so the smallest missing positive is 3.
Input: ([3, 4, -1, 1],)
Expected Output: 2
Explanation: After placing valid values in their correct indices, 1 is present but 2 is missing.
Input: ([7, 8, 9, 11, 12],)
Expected Output: 1
Explanation: No positive number in the useful range [1, n] appears, so 1 is missing.
Input: ([],)
Expected Output: 1
Explanation: An empty array contains no positive integers, so the answer is 1.
Input: ([1],)
Expected Output: 2
Explanation: The array contains 1, so the next missing positive is 2.
Input: ([2, 2, 1],)
Expected Output: 3
Explanation: Both 1 and 2 are present, even though 2 is duplicated, so the smallest missing positive is 3.
Solution
def solution(nums):
n = len(nums)
i = 0
while i < n:
curr = nums[i]
correct_index = curr - 1
if 1 <= curr <= n and nums[i] != nums[correct_index]:
nums[i], nums[correct_index] = nums[correct_index], nums[i]
else:
i += 1
for i in range(n):
if nums[i] != i + 1:
return i + 1
return n + 1Time complexity: O(n). Space complexity: O(1).
Hints
- If the array length is n, the answer must be in the range 1 to n + 1.
- Try placing each value x in index x - 1 whenever 1 <= x <= n.
Part 6: Maximum Subarray Sum
Constraints
- 1 <= len(nums) <= 2 * 10^5
- -10^9 <= nums[i] <= 10^9
- An O(n) solution is expected
Examples
Input: ([-2, 1, -3, 4, -1, 2, 1, -5, 4],)
Expected Output: (6, 3, 6)
Explanation: The best subarray is [4, -1, 2, 1] with sum 6.
Input: ([1],)
Expected Output: (1, 0, 0)
Explanation: Edge case: a single element must be the answer.
Input: ([-1, -2, -3],)
Expected Output: (-1, 0, 0)
Explanation: When all numbers are negative, choose the largest single element.
Input: ([1, -1, 1],)
Expected Output: (1, 0, 0)
Explanation: There are multiple optimal sums of 1; choose the earliest subarray by the tie rule.
Solution
def solution(nums):\n if not nums:\n return (0, -1, -1)\n\n max_sum = nums[0]\n current_sum = nums[0]\n best_start = 0\n best_end = 0\n current_start = 0\n\n for i in range(1, len(nums)):\n if current_sum + nums[i] < nums[i]:\n current_sum = nums[i]\n current_start = i\n else:\n current_sum += nums[i]\n\n if current_sum > max_sum:\n max_sum = current_sum\n best_start = current_start\n best_end = i\n elif current_sum == max_sum:\n if current_start < best_start or (current_start == best_start and i < best_end):\n best_start = current_start\n best_end = i\n\n return (max_sum, best_start, best_end)
Time complexity: O(n). Space complexity: O(1).
Hints
- For each position, decide whether it is better to extend the current subarray or start a new one there.
- Track the start index whenever you reset the running sum.
Part 7: Single-Transaction Stock Profit
Constraints
- 0 <= len(prices) <= 2 * 10^5
- 0 <= prices[i] <= 10^9
- An O(n) one-pass solution is expected
Examples
Input: ([7,1,5,3,6,4],)
Expected Output: 5
Explanation: Buy at price 1 on day 1 and sell at price 6 on day 4 for a profit of 5.
Input: ([7,6,4,3,1],)
Expected Output: 0
Explanation: Prices only decrease, so no profitable transaction is possible.
Input: ([],)
Expected Output: 0
Explanation: With no prices, no transaction can be made.
Input: ([1],)
Expected Output: 0
Explanation: With only one day, you cannot buy and then sell later.
Input: ([2,4,1],)
Expected Output: 2
Explanation: Buy at 2 and sell at 4 for the best profit of 2.
Input: ([3,3,5,0,0,3,1,4],)
Expected Output: 4
Explanation: The best choice is to buy at 0 and sell at 4, giving a profit of 4.
Solution
def solution(prices):
if isinstance(prices, tuple) and len(prices) == 1:
prices = prices[0]
min_price = float('inf')
best = 0
for price in prices:
if price < min_price:
min_price = price
else:
profit = price - min_price
if profit > best:
best = profit
return bestTime complexity: O(n). Space complexity: O(1).
Hints
- As you scan left to right, remember the lowest price seen so far.
- At each day, ask what profit you would make if you sold today.
Part 8: Longest Consecutive Run
Constraints
- 0 <= len(nums) <= 2 * 10^5
- -10^9 <= nums[i] <= 10^9
- An average O(n) solution is expected
Examples
Input: ([100, 4, 200, 1, 3, 2],)
Expected Output: 4
Explanation: The longest consecutive run is [1, 2, 3, 4], which has length 4.
Input: ([0, 3, 7, 2, 5, 8, 4, 6, 0, 1],)
Expected Output: 9
Explanation: The longest consecutive run is [0, 1, 2, 3, 4, 5, 6, 7, 8], which has length 9.
Input: ([],)
Expected Output: 0
Explanation: An empty array contains no consecutive sequence.
Input: ([1, 2, 0, 1],)
Expected Output: 3
Explanation: Duplicates do not extend the run. The longest consecutive run is [0, 1, 2], length 3.
Input: ([-3, -2, -1, 10, 11, 12],)
Expected Output: 3
Explanation: There are two longest runs, [-3, -2, -1] and [10, 11, 12], both of length 3.
Input: ([5],)
Expected Output: 1
Explanation: A single number forms a consecutive run of length 1.
Solution
def solution(nums):
values = set(nums)
best = 0
for x in values:
if x - 1 not in values:
current = x
length = 1
while current + 1 in values:
current += 1
length += 1
if length > best:
best = length
return best
Time complexity: O(n) average. Space complexity: O(n).
Hints
- A number starts a sequence only if the previous number is not present.
- Using a set lets you check whether neighbors exist in average O(1) time.
Part 9: Course Completion Feasibility
Constraints
- 1 <= numCourses <= 10^5
- 0 <= len(prerequisites) <= 10^5
- 0 <= a, b < numCourses
Examples
Input: (2, [[1, 0]])
Expected Output: (True, [0, 1])
Explanation: Course 0 has no prerequisites, so take it first. Then course 1 becomes available.
Input: (4, [[1, 0], [2, 0], [3, 1], [3, 2]])
Expected Output: (True, [0, 1, 2, 3])
Explanation: After taking course 0, both 1 and 2 are available. Choose 1 first because it is smaller, then 2, then 3.
Input: (2, [[1, 0], [0, 1]])
Expected Output: (False, [])
Explanation: There is a cycle between courses 0 and 1, so no valid ordering exists.
Input: (4, [[2, 0], [2, 1]])
Expected Output: (True, [0, 1, 2, 3])
Explanation: Initially 0, 1, and 3 are available. Choose 0, then 1, which unlocks 2. Since 2 is smaller than 3, it is taken before 3.
Input: (3, [])
Expected Output: (True, [0, 1, 2])
Explanation: With no prerequisites, all courses are immediately available, so return them in increasing order.
Input: (0, [])
Expected Output: (True, [])
Explanation: With zero courses, it is trivially possible to finish everything.
Solution
def solution(numCourses, prerequisites):
import heapq
graph = [[] for _ in range(numCourses)]
indegree = [0] * numCourses
for course, prereq in prerequisites:
graph[prereq].append(course)
indegree[course] += 1
heap = [i for i in range(numCourses) if indegree[i] == 0]
heapq.heapify(heap)
order = []
while heap:
current = heapq.heappop(heap)
order.append(current)
for neighbor in graph[current]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
heapq.heappush(heap, neighbor)
if len(order) == numCourses:
return (True, order)
return (False, [])Time complexity: O((numCourses + len(prerequisites)) log numCourses). Space complexity: O(numCourses + len(prerequisites)).
Hints
- Think of courses as a directed graph and prerequisites as edges.
- If you repeatedly remove zero-indegree nodes and cannot process all nodes, a cycle exists.