時間制限付き模擬面接:EasyとMediumの問題
45分の制限時間で3問を解き、実際の面接と同じように思考過程を言葉にして説明し、その後で最適解を振り返ります。
「時間制限付き模擬面接:EasyとMediumの問題」はCoddyKit上の無料DSA Interview Prepレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはDSA Interview Prep学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 DSA Interview Prepコースには全4レッスンが含まれています。
この模擬面接の進め方
このレッスンでは、実際のコーディング面接をシミュレーションします。各問題では、(1)一度読んで、(2)60秒以内にパターンを特定し、(3)アプローチと計算量を述べ、(4)解答を書き、(5)例を使ってテストしてください。タイマーを設定しましょう。簡単な問題は10〜15分、中程度の問題は20〜25分で解くことを目標にしてください。
解答を先に見ないでください。それでは練習の目的が失われます。5分経っても行き詰まっている場合は、問題文を読み直し、パターンを示すシグナルワード(ソート済みか、最小か、すべての組み合わせか、部分配列か)を探してください。自力で行き詰まりを解消する能力は、素早く解く能力と同じくらい重要です。
# 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')簡単な問題1:有効な括弧
問題:'('、')'、'{'、'}'、'['、']'のみを含む文字列が与えられたとき、入力文字列が有効かどうかを判定してください。すべての開き括弧が、同じ種類の括弧で正しい順序に閉じられている場合、その文字列は有効です。
シグナル:ペアの対応、順序が重要、最後に開いた括弧を最初に閉じる必要がある → スタック。開き括弧をプッシュし、閉じ括弧が来たらポップして確認します。ポップしようとしたときにスタックが空の場合、または最後に要素が残っている場合、文字列は無効です。時間計算量O(n)、空間計算量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})')簡単な問題2:株式の売買に最適なタイミング
問題:prices配列が与えられ、prices[i]はi日目の株価を表します。1回の購入と1回の売却(購入は売却より前でなければなりません)によって得られる最大利益を求めてください。利益を得られない場合は0を返します。
シグナル:左側の要素が右側の要素より前にある最大差 → 左から右に走査しながら、これまでの最小値を記録します。各日について、得られる可能性のある利益はcurrent_price - min_so_farです。最大利益を更新します。これはO(n)/O(1)で、Kadaneのアルゴリズムの特殊なケースです。
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})')中程度の問題1:3Sum
問題:配列が与えられたとき、合計が0になる一意な3要素組をすべて求めてください。解答に重複する3要素組を含めてはいけません。
パターン:ツーポインタ法を3要素に拡張します。配列をソートします。各要素nums[i]について、left = i+1、right = n-1の2つのポインタを使い、合計が-nums[i]になるペアを探します。同じ値を通り過ぎるまでポインタを進めて、重複をスキップします。時間計算量O(n²)、出力を除く空間計算量O(1)です。ソートすることで、重複をきれいに処理できます。
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])) # []中級問題 2:繰り返し文字のない最長部分文字列
問題:文字列が与えられたとき、文字が重複しない最長部分文字列の長さを求めてください。
パターン:集合(または最後に現れた位置を記録する辞書)を使ったスライディングウィンドウです。ウィンドウ [left, right] を維持します。各文字を含めながら right を右に広げます。文字が重複した場合(すでにウィンドウ内に存在する場合)は、重複がなくなるまで left から縮めます。これまでに確認したウィンドウの最大サイズを記録します。時間計算量は O(n)、空間計算量は 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})')中級問題 3:コインチェンジ
問題:コインの額面と目標金額が与えられたとき、目標金額に達するために必要なコインの最小枚数を求めてください。不可能な場合は -1 を返します。
パターン:典型的な1次元 DP(非制限ナップサック問題の変形)です。dp[i] は金額 i に必要なコインの最小枚数を表します。dp[0] = 0、その他すべてを無限大に初期化します。1から目標金額までの各金額について、すべてのコインの額面を試します。有効な各コインについて、dp[i] = min(dp[i], dp[i - coin] + 1) と更新します。時間計算量は O(amount × len(coins))、空間計算量は 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})')時間制限下での問題解決ワークフロー
時間がなくなってきたら、次の優先順位で進めてください:(1) 不完全な最適解よりも、正しい出力を返す動作するブルートフォース解、(2) エッジケースを明示的に処理すること、(3) 巧妙なワンライナーよりも、読みやすく整ったコードを書くこと。面接官は、わかりにくいバグを含む O(n) 解よりも、すべてのテストケースに合格する整った O(n²) 解を高く評価します。
O(n²) 解が間違っていることに気づいても、途中で放棄しないでください。まず完成させてテストし、時間が残っていれば最適化を提案してください。途中までしか書かれていない最適解は、完全ではあるものの最適でない解よりも評価が低くなります。
# 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 []解答のレビュー:5つの質問
「終わりました」と言う前に、次の5つの質問を自分に問いかけてください。
- 空の入力を処理できますか?
[]、''、None、n=0 - 要素が1つだけの場合を処理できますか? サイズ1の配列、ノードが1つの木
- すべての要素が同じ場合を処理できますか?
[5, 5, 5, 5]、'aaaa' - 最小値と最大値を処理できますか? 負の数、非常に大きな整数、0
- 時間計算量と空間計算量を説明しましたか? 簡潔な根拠を添えた Big-O
この5つの確認で、面接の解答に含まれるバグの大半を見つけられます。面接官は候補者が自分でテストすることを期待しています。フィードバックを求めない限り、解答にバグがあることを教えてくれるとは限りません。
# 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})')追加質問への対応
問題を解いた後、面接官は通常、追加の質問をします。よくある質問の種類は次のとおりです。
- 「O(1)の空間計算量でできますか?」 → 入力をその場で変更する方法や数学的な工夫を探します
- 「nが非常に大きい場合はどうしますか?」 → ストリーミング、ページネーション、サンプリングの方法について検討します
- 「配列がすでにソートされている場合はどうしますか?」 → より単純なアルゴリズムが存在することがよくあります
- 「並列化できますか?」 → 独立したサブ問題を特定し、MapReduce やタスク並列化について検討します
追加質問では、知識の深さと適応力が試されます。すぐに推測して答えるのではなく、「少し考えさせてください」と言いましょう。自信ありげに間違った答えをするより、よく考えるために一呼吸置くほうがよい対応です。
# 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')練習問題:アナグラムのグループ化
問題:文字列の配列が与えられたとき、アナグラムをまとめてグループ化してください。グループのリストを返します。
パターン:キーとして頻度マップを使用します。各文字列について、文字をソートするか、文字の出現頻度のタプルを計算して正規化キーを作成します。ハッシュマップとリストを使って、このキーごとに文字列をグループ化します。mを文字列の最大長とすると、時間計算量は O(n × m log m)、空間計算量は O(n × m) です。配列を1回走査するだけなので、入れ子のループは必要ありません。
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)])模擬面接後の自己評価
模擬面接のたびに、次の観点で自分を評価してください。
- パターン認識の速さ:60秒未満でパターンを特定できましたか?
- コードの正確性:最初の解答ですべてのテストケースに合格しましたか?
- エッジケースへの対応:空の入力、要素が1つの入力、極端な入力をテストしましたか?
- コミュニケーション:考え方を途中で説明し続けましたか?
- 計算量への意識:時間計算量と空間計算量を説明しましたか?
- 立て直し:行き詰まったとき、柔軟に方針転換できましたか、それとも固まってしまいましたか?
各観点を1〜5で評価してください。次の1週間の練習では、最も評価が低かった観点に重点を置きます。多くの候補者が改善すべきなのは、パターン認識かコミュニケーションのどちらかです。両方を同時に改善する必要があることは、ほとんどありません。
# 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 scoresクイックチェック
このレッスンで学んだ Data Structures & Algorithms — Coding Interview Prep の概念について、理解度を確認しましょう。
レッスンのまとめ
このレッスンでは、決まったワークフロー(問題を読み、60秒でパターンを特定し、計算量を説明し、コードを書き、5つのエッジケースのカテゴリでテストする)で問題に取り組むこと、時間がなくなってきたときは、不完全な最適解よりも動作するブルートフォース解のほうが優れていること、そして各模擬練習セッション後に、6つの観点(速さ、正確性、エッジケース、コミュニケーション、計算量、立て直し)で自己評価すると、適切な分野に改善の焦点を当てられることを学びました。次は、エッジケースへの対応と、面接でのコミュニケーションのベストプラクティスについて詳しく学びます。
よくある質問
「時間制限付き模擬面接:EasyとMediumの問題」レッスンは無料ですか?
はい。「時間制限付き模擬面接:EasyとMediumの問題」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、DSA Interview Prepコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 DSA Interview Prepコースには全4レッスンが含まれています。
「時間制限付き模擬面接:EasyとMediumの問題」で何を学びますか?
45分の制限時間で3問を解き、実際の面接と同じように思考過程を言葉にして説明し、その後で最適解を振り返ります。 ブラウザで直接実行するハンズオンコードでDSA Interview Prepを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
DSA Interview Prepを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのDSA Interview Prepは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「時間制限付き模擬面接:EasyとMediumの問題」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このDSA Interview Prepレッスンでコードを書いて実行できますか?
はい。すべてのDSA Interview Prepレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- パターン認識チートシート
- 時間制限付き模擬面接:EasyとMediumの問題
- エッジケースへの対応と面接での意思疎通
- 難問の解説:Word Ladder IIとAlien Dictionary