0Pricing
Coding Interview Prep · Lezione

Colloquio simulato a tempo: problemi facili e medi

Risolva tre problemi entro un limite di 45 minuti, esponga il proprio ragionamento come farebbe in un vero colloquio e riveda in seguito le soluzioni ottimali.

Colloquio simulato a tempo: problemi facili e medi è una lezione Coding Interview Prep gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Coding Interview Prep, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Coding Interview Prep include 4 lezioni in totale.

Come utilizzare questa simulazione di colloquio

Questa lezione simula una vera sessione di coding interview. Per ogni problema, dovrà: (1) leggerlo una volta, (2) identificare il pattern entro 60 secondi, (3) dichiarare il proprio approccio e la complessità, (4) scrivere la soluzione e (5) verificarla con alcuni esempi. Imposti un timer. Un problema facile dovrebbe richiedere 10-15 minuti; un problema di difficoltà media, 20-25 minuti.

Non guardi in anticipo la soluzione: vanificherebbe lo scopo dell'esercizio. Se rimane bloccato dopo 5 minuti, rilegga la descrizione del problema e cerchi la parola chiave che rivela il pattern (ordinato? minimo? tutte le combinazioni? subarray?). La capacità di sbloccarsi autonomamente è importante quanto quella di risolvere rapidamente il problema.

# 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')

Problema facile 1: parentesi valide

Problema: data una stringa contenente esclusivamente '(', ')', '{', '}', '[', ']', determinare se la stringa di input è valida. Una stringa è valida se ogni parentesi aperta viene chiusa da una parentesi dello stesso tipo e nell'ordine corretto.

Segnale: coppie corrispondenti, l'ordine è importante, la parentesi aperta più recente deve essere chiusa per prima → Stack. Inserisca le parentesi aperte nello stack; estragga gli elementi e verifichi le parentesi di chiusura. Se lo stack è vuoto quando si tenta di estrarre un elemento, oppure contiene ancora elementi alla fine, la stringa non è valida. Tempo O(n), spazio 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})')

Problema facile 2: momento migliore per acquistare e vendere azioni

Problema: dato un array prices in cui prices[i] è il prezzo dell'azione al giorno i, trovare il profitto massimo effettuando un solo acquisto e una sola vendita (è necessario acquistare prima di vendere). Restituire 0 se non è possibile ottenere un profitto.

Segnale: differenza massima in cui l'elemento a sinistra deve precedere quello a destra → tenga traccia del minimo progressivo durante la scansione da sinistra a destra. Ogni giorno, il profitto potenziale è current_price - min_so_far. Aggiorni il profitto massimo. Questo approccio è O(n)/O(1) ed è un caso particolare dell'algoritmo di 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})')

Problema di difficoltà media 1: Three Sum

Problema: dato un array, trovare tutte le triplette uniche la cui somma è zero. La soluzione non deve contenere triplette duplicate.

Pattern: two pointers esteso a tre elementi. Ordini l'array. Per ogni elemento nums[i], utilizzi due puntatori left = i+1, right = n-1 per trovare coppie la cui somma è -nums[i]. Salti i duplicati avanzando oltre i valori identici. Tempo O(n²), spazio O(1) escluso l'output. L'ordinamento semplifica la gestione dei duplicati.

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]))           # []

Problema di difficoltà media 2: sottostringa più lunga senza caratteri ripetuti

Problema: Data una stringa, determini la lunghezza della sottostringa più lunga senza caratteri ripetuti.

Pattern: Finestra scorrevole con un insieme (o un dizionario delle ultime posizioni). Mantenga una finestra [left, right]. Espanda right includendo ogni carattere. Se un carattere si ripete (è già nella finestra), riduca la finestra da sinistra finché il duplicato non viene eliminato. Tenga traccia della dimensione massima della finestra osservata. Tempo O(n), spazio 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})')

Problema di difficoltà media 3: resto delle monete

Problema: Date le denominazioni delle monete e un importo obiettivo, determini il numero minimo di monete necessario per raggiungere tale importo. Restituisca -1 se è impossibile.

Pattern: DP classica monodimensionale (variante del problema dello zaino illimitato). dp[i] = numero minimo di monete per l'importo i. Inizializzi dp[0] = 0 e tutti gli altri valori a infinito. Per ogni importo da 1 a target, provi tutte le denominazioni delle monete. Per ogni moneta valida, applichi dp[i] = min(dp[i], dp[i - coin] + 1). Tempo O(amount × len(coins)), spazio 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})')

Flusso di lavoro per risolvere i problemi sotto pressione

Quando il tempo sta per scadere, dia priorità, nell'ordine, a: (1) una soluzione a forza bruta funzionante con output corretto invece di una soluzione ottimale incompleta, (2) la gestione evidente dei casi limite, (3) codice pulito e leggibile anziché one-liner ingegnose. Gli intervistatori preferiscono una soluzione pulita O(n²) che superi tutti i casi di test rispetto a una soluzione O(n) con un bug difficile da individuare.

Se si accorge che la soluzione O(n²) non è corretta, non la abbandoni a metà: la completi, la testi e poi si offra di ottimizzarla se rimane tempo. Una soluzione ottimale scritta solo a metà dà meno credito di una soluzione completa ma non ottimale.

# 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 []

Revisione della soluzione: cinque domande

Prima di dire 'Ho finito', si ponga queste cinque domande:

  1. Gestisce l'input vuoto? [], '', None, n=0
  2. Gestisce un singolo elemento? Array di dimensione 1, alberi con un solo nodo
  3. Gestisce elementi tutti uguali? [5, 5, 5, 5], 'aaaa'
  4. Gestisce i valori minimi e massimi? Numeri negativi, interi molto grandi, 0
  5. Ha indicato la complessità temporale e spaziale? Big-O con una breve motivazione

Questi cinque controlli individuano la maggior parte dei bug nelle soluzioni dei colloqui. Gli intervistatori si aspettano che i candidati eseguano test autonomamente: non segnaleranno un bug nella soluzione a meno che non venga richiesto un 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})')

Gestire le domande di approfondimento

Dopo aver risolto il problema, gli intervistatori pongono tipicamente domande di approfondimento. I tipi più comuni sono:

  • 'Può farlo usando spazio O(1)?' → Cerchi modifiche in-place o strategie matematiche
  • 'Cosa succede se n è molto grande?' → Discuta approcci di streaming, paginazione o campionamento
  • 'Cosa succede se l'array è già ordinato?' → Spesso esiste un algoritmo più semplice
  • 'Può parallelizzare questa operazione?' → Individui i sottoproblemi indipendenti e discuta MapReduce o il parallelismo delle attività

Le domande di approfondimento verificano la profondità delle conoscenze e la capacità di adattamento. Dica 'Mi lasci riflettere un momento' invece di tirare subito a indovinare. Una pausa ponderata è preferibile a una risposta sbagliata data con sicurezza.

# 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')

Problema di esercitazione: raggruppare gli anagrammi

Problema: Dato un array di stringhe, raggruppi tra loro gli anagrammi. Restituisca un elenco di gruppi.

Pattern: Mappa delle frequenze come chiave. Per ogni stringa, ordini i caratteri (oppure calcoli una tupla delle frequenze dei caratteri) per ottenere la chiave canonica. Raggruppi le stringhe in base a questa chiave usando una mappa hash di liste. Tempo O(n × m log m), dove m è la lunghezza massima delle stringhe, spazio O(n × m). Non sono necessari cicli annidati: basta un'unica scansione dell'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)])

Autovalutazione dopo un colloquio simulato

Dopo ogni colloquio simulato, si valuti secondo queste dimensioni:

  • Velocità nel riconoscimento dei pattern: Ha identificato il pattern in <60 secondi?
  • Correttezza del codice: La prima soluzione ha superato tutti i casi di test?
  • Gestione dei casi limite: Ha testato input vuoti, singoli ed estremi?
  • Comunicazione: Ha spiegato il ragionamento durante tutto il processo?
  • Consapevolezza della complessità: Ha indicato la complessità temporale e spaziale?
  • Capacità di recupero: Se si è bloccato, ha cambiato approccio con naturalezza oppure si è irrigidito?

Si assegni un punteggio da 1 a 5 per ogni dimensione. Concentri la pratica della settimana successiva sulla dimensione con il punteggio più basso. La maggior parte dei candidati deve migliorare il riconoscimento dei pattern oppure la comunicazione, raramente entrambi.

# 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

Verifica rapida

Verifichi la comprensione dei concetti di Data Structures & Algorithms — Coding Interview Prep presentati in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato a: seguire un flusso di lavoro fisso per affrontare i problemi: leggere, individuare il pattern in 60 secondi, dichiarare la complessità, scrivere il codice e infine testare cinque categorie di casi limite, una soluzione a forza bruta funzionante è preferibile a una soluzione ottimale incompleta quando il tempo sta per scadere e l'autovalutazione dopo ogni sessione di pratica simulata, secondo sei dimensioni (velocità, correttezza, casi limite, comunicazione, complessità e capacità di recupero), concentra il miglioramento sulle aree giuste. Nella prossima lezione tratteremo in modo approfondito la gestione dei casi limite e le migliori pratiche di comunicazione del candidato al colloquio.

Domande Frequenti

La lezione «Colloquio simulato a tempo: problemi facili e medi» è gratuita?

Sì — il testo completo di «Colloquio simulato a tempo: problemi facili e medi» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Coding Interview Prep, passa a CoddyKit PRO. Il corso Coding Interview Prep include 4 lezioni in totale.

Cosa imparerò in «Colloquio simulato a tempo: problemi facili e medi»?

Risolva tre problemi entro un limite di 45 minuti, esponga il proprio ragionamento come farebbe in un vero colloquio e riveda in seguito le soluzioni ottimali. Eserciti Coding Interview Prep con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Coding Interview Prep?

Non è richiesta alcuna esperienza precedente. Coding Interview Prep su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Colloquio simulato a tempo: problemi facili e medi»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Coding Interview Prep?

Sì. Ogni lezione Coding Interview Prep include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Scheda di riferimento per il riconoscimento dei pattern
  2. Colloquio simulato a tempo: problemi facili e medi
  3. Gestione dei casi limite e comunicazione durante il colloquio
  4. Analisi guidata di problemi difficili: Word Ladder II e Alien Dictionary
← Torna a Coding Interview Prep