Timed Mock Interview: Easy and Medium Problems
Solve three problems under a 45-minute timer, verbalise your thought process as you would in a real interview, and review optimal solutions afterward.
Timed Mock Interview: Easy and Medium Problems is a free DSA Interview Prep lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the DSA Interview Prep learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
How to Use This Mock Interview
This lesson simulates a real coding interview session. For each problem, you should: (1) read it once, (2) identify the pattern within 60 seconds, (3) state your approach and complexity, (4) write the solution, and (5) test with examples. Set a timer. An easy problem should take 10-15 minutes; a medium problem 20-25 minutes.
Do not look ahead at the solution — it defeats the purpose. If you are stuck after 5 minutes, re-read the problem statement and look for the signal word that reveals the pattern (sorted? minimum? all combinations? subarray?). The ability to self-unstick is as important as the ability to solve quickly.
# Mock interview timer simulation
import time
class InterviewTimer:
def __init__(self, total_minutes):
self.total = total_minutes * 60
self.start = None
def begin(self, problem_name):
self.start = time.time()
print(f'TIMER STARTED: {problem_name}')
print(f'You have {self.total//60} minutes. Go!')
def checkpoint(self, label):
if self.start:
elapsed = time.time() - self.start
remaining = self.total - elapsed
print(f'[{label}] Elapsed: {elapsed:.0f}s, Remaining: {remaining:.0f}s')
# Usage in real practice:
timer = InterviewTimer(15) # 15-minute easy problem
timer.begin('Two Sum')
time.sleep(1)
timer.checkpoint('Identified pattern')Easy Problem 1: Valid Parentheses
Problem: Given a string containing only '(', ')', '{', '}', '[', ']', determine if the input string is valid. A string is valid if every open bracket is closed by the same type of bracket in the correct order.
Signal: Matching pairs, ordering matters, most recent open bracket must close first → Stack. Push open brackets; pop and verify on close brackets. If the stack is empty when we try to pop, or has leftover items at the end, the string is invalid. Time O(n), Space O(n).
def is_valid(s):
stack = []
matching = {')': '(', '}': '{', ']': '['}
for char in s:
if char in '({[':
stack.append(char)
else:
if not stack or stack[-1] != matching[char]:
return False
stack.pop()
return len(stack) == 0
# Test cases
test_cases = [
('()', True),
('()[]{}' , True),
('(]', False),
('([)]', False),
('{[]}', True),
('', True), # empty string is valid
('(((', False), # unmatched opens
(')]', False), # close without open
]
for s, expected in test_cases:
result = is_valid(s)
status = 'PASS' if result == expected else 'FAIL'
print(f'{status}: is_valid({repr(s)}) = {result} (expected {expected})')Easy Problem 2: Best Time to Buy and Sell Stock
Problem: Given an array prices where prices[i] is the stock price on day i, find the maximum profit from one buy and one sell (must buy before sell). Return 0 if no profit is possible.
Signal: Maximum difference where left must precede right → Track the running minimum as you scan left to right. At each day, the potential profit is current_price - min_so_far. Update the maximum profit. This is O(n)/O(1) and is a special case of Kadane's algorithm.
def max_profit(prices):
if not prices:
return 0
min_price = float('inf')
max_profit = 0
for price in prices:
if price < min_price:
min_price = price
elif price - min_price > max_profit:
max_profit = price - min_price
return max_profit
# Test cases
test_cases = [
([7, 1, 5, 3, 6, 4], 5), # buy at 1, sell at 6
([7, 6, 4, 3, 1], 0), # monotonically decreasing: no profit
([2, 4, 1], 2), # buy at 2, sell at 4
([1], 0), # single price: no transaction possible
([3, 3, 3], 0), # flat: no profit
]
for prices, expected in test_cases:
result = max_profit(prices)
status = 'PASS' if result == expected else 'FAIL'
print(f'{status}: max_profit({prices}) = {result} (expected {expected})')Medium Problem 1: Three Sum
Problem: Given an array, find all unique triplets that sum to zero. The solution must not contain duplicate triplets.
Pattern: Two-pointer extended to three elements. Sort the array. For each element nums[i], use two pointers left = i+1, right = n-1 to find pairs summing to -nums[i]. Skip duplicates by advancing past identical values. Time O(n²), Space O(1) excluding output. The sort makes duplicate handling clean.
def three_sum(nums):
nums.sort()
result = []
n = len(nums)
for i in range(n - 2):
# Skip duplicate values for the first element
if i > 0 and nums[i] == nums[i - 1]:
continue
left, right = i + 1, n - 1
while left < right:
total = nums[i] + nums[left] + nums[right]
if total == 0:
result.append([nums[i], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]:
left += 1 # skip duplicate lefts
while left < right and nums[right] == nums[right - 1]:
right -= 1 # skip duplicate rights
left += 1; right -= 1
elif total < 0:
left += 1
else:
right -= 1
return result
print(three_sum([-1, 0, 1, 2, -1, -4])) # [[-1,-1,2],[-1,0,1]]
print(three_sum([0, 0, 0, 0])) # [[0,0,0]]
print(three_sum([])) # []
print(three_sum([1, 2, -2, -1])) # []Medium Problem 2: Longest Substring Without Repeating Characters
Problem: Given a string, find the length of the longest substring without repeating characters.
Pattern: Sliding window with a set (or dict of last positions). Maintain a window [left, right]. Expand right by including each character. If a character repeats (already in window), shrink from the left until the duplicate is removed. Track the maximum window size seen. Time O(n), Space O(min(n, alphabet_size)).
def length_of_longest_substring(s):
char_index = {} # character -> last seen index
left = 0
max_len = 0
for right, char in enumerate(s):
if char in char_index and char_index[char] >= left:
left = char_index[char] + 1 # shrink window past duplicate
char_index[char] = right
max_len = max(max_len, right - left + 1)
return max_len
# Test cases
test_cases = [
('abcabcbb', 3), # 'abc'
('bbbbb', 1), # 'b'
('pwwkew', 3), # 'wke'
('', 0), # empty string
('au', 2), # full string
('dvdf', 3), # 'vdf' (skip the first d)
]
for s, expected in test_cases:
result = length_of_longest_substring(s)
status = 'PASS' if result == expected else 'FAIL'
print(f'{status}: len_longest({repr(s)}) = {result} (expected {expected})')Medium Problem 3: Coin Change
Problem: Given coin denominations and a target amount, find the minimum number of coins needed to reach the amount. Return -1 if it is impossible.
Pattern: Classic 1D DP (unbounded knapsack variant). dp[i] = minimum coins for amount i. Initialise dp[0] = 0, all others = infinity. For each amount from 1 to target, try all coin denominations. dp[i] = min(dp[i], dp[i - coin] + 1) for each valid coin. Time O(amount × len(coins)), Space O(amount).
def coin_change(coins, amount):
dp = [float('inf')] * (amount + 1)
dp[0] = 0 # 0 coins to make amount 0
for i in range(1, amount + 1):
for coin in coins:
if coin <= i and dp[i - coin] + 1 < dp[i]:
dp[i] = dp[i - coin] + 1
return dp[amount] if dp[amount] != float('inf') else -1
# Test cases
test_cases = [
([1, 5, 11], 15, 3), # 11+1+1+1+1... wait: 11+1+1+1+1=5 coins? No: 5+5+5=3
([2], 3, -1), # impossible (only even coins)
([1], 0, 0), # 0 coins for amount 0
([1, 2, 5], 11, 3), # 5+5+1
([186, 419, 83, 408], 6249, 20), # stress test
]
for coins, amount, expected in test_cases:
result = coin_change(coins, amount)
status = 'PASS' if result == expected else 'FAIL'
print(f'{status}: coin_change({coins}, {amount}) = {result} (expected {expected})')Problem-Solving Workflow Under Time Pressure
When time is running out, prioritise in this order: (1) a working brute-force solution with correct output over an incomplete optimal solution, (2) handle edge cases visibly, (3) write clean readable code rather than clever one-liners. Interviewers prefer a clean O(n²) solution that passes all test cases over an O(n) solution with a subtle bug.
If you realise your O(n²) solution is wrong, do not abandon it midway — finish it, test it, then offer to optimise if time remains. A half-written optimal solution earns less credit than a complete but suboptimal one.
# Priority order when time runs out
priority = [
('First priority', 'Correct brute-force that passes all test cases'),
('Second priority', 'Optimal solution with bugs is WORSE than suboptimal correct'),
('Third priority', 'Edge cases handled visibly (empty input, single element, negatives)'),
('Fourth priority', 'Clean variable names and readable code'),
('Fifth priority', 'Add complexity statement as a comment at the top'),
]
print('Under time pressure, prioritise:')
for priority_level, desc in priority:
print(f' {priority_level}: {desc}')
# Adding complexity as a comment
def two_sum_commented(nums, target):
# Time: O(n), Space: O(n)
seen = {}
for i, n in enumerate(nums):
complement = target - n
if complement in seen:
return [seen[complement], i]
seen[n] = i
return []Reviewing Your Solution: Five Questions
Before saying 'I'm done', ask yourself these five questions:
- Does it handle empty input?
[],'',None, n=0 - Does it handle a single element? Arrays of size 1, trees with one node
- Does it handle all-same elements?
[5, 5, 5, 5],'aaaa' - Does it handle minimum and maximum values? Negative numbers, very large integers, 0
- Have I stated the time and space complexity? Big-O with a brief justification
These five checks catch the majority of bugs in interview solutions. Interviewers expect candidates to self-test — they will not tell you your solution has a bug unless you ask for feedback.
# The five edge-case categories with examples
edge_cases = {
'Empty input': ['[] empty array', '"" empty string', 'None / null'],
'Single element': ['[42]', 'single node tree', 'n=1'],
'All same': ['[3,3,3,3]', '"aaaa"', 'uniform grid'],
'Extreme values': ['[-10^9, 10^9]', 'INT_MAX + 1 overflow check', '0 as input'],
'Already sorted': ['ascending + descending', 'already optimal input'],
}
for category, examples in edge_cases.items():
print(f'{category}:')
for ex in examples:
print(f' - {ex}')
print()
# Template for self-testing:
def test_my_solution(fn, test_cases):
for inputs, expected in test_cases:
result = fn(*inputs) if isinstance(inputs, tuple) else fn(inputs)
status = 'PASS' if result == expected else 'FAIL'
print(f'{status}: {inputs} => {result} (expected {expected})')Handling Follow-Up Questions
After you solve the problem, interviewers typically ask follow-up questions. Common types:
- 'Can you do it in O(1) space?' → Look for in-place modification or mathematical tricks
- 'What if n is very large?' → Discuss streaming, pagination, or sampling approaches
- 'What if the array is already sorted?' → Simpler algorithm often exists
- 'Can you parallelize this?' → Identify independent sub-problems and discuss MapReduce or task parallelism
Follow-up questions test depth and adaptability. Say 'Let me think for a moment' rather than immediately guessing. A thoughtful pause is better than a wrong answer delivered confidently.
# Follow-up answers for classic problems
follow_ups = [
{
'problem': 'Find duplicate in array 1..n (space O(n) solution uses set)',
'follow_up': 'Can you do it in O(1) space without modifying input?',
'answer': 'Floyd cycle detection: treat array as linked list (slow/fast pointer)',
},
{
'problem': 'Reverse a string (space O(n) with new array)',
'follow_up': 'Can you do it in-place?',
'answer': 'Two pointers from both ends, swap until they meet: O(n) time O(1) space',
},
{
'problem': 'Find max in array: O(n) single pass',
'follow_up': 'What if the array is streamed one element at a time?',
'answer': 'Same algorithm works! Running maximum handles infinite streams',
},
{
'problem': 'Merge sorted arrays O(n+m)',
'follow_up': 'What if you have K sorted arrays?',
'answer': 'Use a min-heap of (value, array_idx, element_idx): O(n log k)',
},
]
for fu in follow_ups:
print(f'Problem: {fu["problem"]}')
print(f'Follow-up: {fu["follow_up"]}')
print(f'Answer: {fu["answer"]}\n')Practice Problem: Group Anagrams
Problem: Given an array of strings, group the anagrams together. Return a list of groups.
Pattern: Frequency map as key. For each string, sort its characters (or compute a character frequency tuple) as the canonical key. Group strings by this key using a hash map of lists. Time O(n × m log m) where m is the max string length, Space O(n × m). No nested loops needed — one pass through the array.
from collections import defaultdict
def group_anagrams(strs):
# Method 1: sort each string as key
groups = defaultdict(list)
for s in strs:
key = ''.join(sorted(s)) # canonical form
groups[key].append(s)
return list(groups.values())
def group_anagrams_v2(strs):
# Method 2: character count tuple as key (avoids sorting)
groups = defaultdict(list)
for s in strs:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
key = tuple(count) # immutable, hashable
groups[key].append(s)
return list(groups.values())
test = ['eat', 'tea', 'tan', 'ate', 'nat', 'bat']
result = [sorted(g) for g in group_anagrams(test)]
result.sort()
print('Groups:', result)
# [['ate','eat','tea'], ['bat'], ['nat','tan']]
print('V2:', [sorted(g) for g in sorted(group_anagrams_v2(test), key=len)])Self-Assessment After a Mock
After each mock interview, evaluate yourself on these dimensions:
- Pattern recognition speed: Did you identify the pattern in <60 seconds?
- Code correctness: Did your first solution pass all test cases?
- Edge case handling: Did you test empty/single/extreme inputs?
- Communication: Did you explain your reasoning throughout?
- Complexity awareness: Did you state time and space complexity?
- Recovery: If stuck, did you pivot gracefully or freeze?
Rate yourself 1-5 on each dimension. Focus your next week of practice on the lowest-rated dimension. Most candidates need to improve either pattern recognition or communication — rarely both.
# Self-assessment scoring template
def self_assess(pattern_speed, code_correctness, edge_cases,
communication, complexity, recovery):
scores = {
'Pattern recognition (< 60s)': pattern_speed,
'Code correctness (all tests pass)': code_correctness,
'Edge case handling': edge_cases,
'Communication (thinking aloud)': communication,
'Complexity stated correctly': complexity,
'Recovery when stuck': recovery,
}
total = sum(scores.values())
max_total = len(scores) * 5
print('Self-Assessment Results:')
print('-'*50)
for dim, score in scores.items():
bar = '#' * score + '-' * (5 - score)
print(f'{dim:45s} [{bar}] {score}/5')
print(f'\nTotal: {total}/{max_total} ({total/max_total*100:.0f}%)')
weak = min(scores, key=scores.get)
print(f'Focus area: {weak}')
self_assess(4, 3, 4, 3, 5, 2) # example scoresQuick Check
Test your understanding of Data Structures & Algorithms — Coding Interview Prep concepts from this lesson.
Lesson Recap
In this lesson you learned: approach problems with a fixed workflow — read, identify pattern in 60 seconds, state complexity, code, then test with five edge-case categories, a working brute-force solution beats an incomplete optimal solution when time is running out, and self-assessment after each mock practice session on six dimensions (speed, correctness, edges, communication, complexity, recovery) focuses improvement on the right areas. Next up we cover handling edge cases and interviewee communication best practices in depth.
Frequently asked questions
Is the “Timed Mock Interview: Easy and Medium Problems” lesson free?
Yes — the full text of “Timed Mock Interview: Easy and Medium Problems” is free to read here on the web, and the DSA Interview Prep course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the DSA Interview Prep course, upgrade to CoddyKit PRO.
What will I learn in “Timed Mock Interview: Easy and Medium Problems”?
Solve three problems under a 45-minute timer, verbalise your thought process as you would in a real interview, and review optimal solutions afterward. You practise DSA Interview Prep with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start DSA Interview Prep?
No prior experience is required. DSA Interview Prep on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Timed Mock Interview: Easy and Medium Problems” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this DSA Interview Prep lesson?
Yes. Every DSA Interview Prep lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Pattern Recognition Cheat Sheet
- Timed Mock Interview: Easy and Medium Problems
- Handling Edge Cases and Interviewee Communication
- Hard Problem Walkthroughs: Word Ladder II and Alien Dictionary