LeetCode: Imparare le strutture dati e gli algoritmi

LeetCode

Nell’ambito del mio programma di sviluppo personale in Bitrock come Junior Frontend Developer, volevo addentrarmi nel fantastico mondo dei DSA (Data Structures and Algorithms – Algoritmi e Strutture Dati), cercando di risolvere quei problemi apparentemente impossibili del LeetCode e applicando quegli algoritmi in un progetto reale. Così sono partito, sapendo che la strada sarebbe stata dura, ma con la ferma intenzione di riuscire a conquistare i DSA.

Cos è LeetCode?

LeetCode è una famosa piattaforma utilizzata dagli sviluppatori per affinare le proprie competenze in materia di strutture dati e algoritmi. Offre un’ampia gamma di problemi, da compiti di base come la somma di array a sfide più complesse che coinvolgono strutture di dati intricate come gli alberi.

Dall’emozione al burnout

Tornando alla mia esperienza di apprendimento, ho iniziato con un corso su Udemy chiamato “Javascript Algorithm and Data Structures Masterclass”. Sapevo bene che Javascript non era il miglior linguaggio di programmazione per risolvere i problemi DSA, ma all’epoca non ero interessato a imparare un altro linguaggio solo per risolvere i problemi di LeetCode. Mentre guardavo il corso, mi sono detto: “Non è poi così male, posso risolvere questi problemi senza l’aiuto dell’istruttore”, ma mi sbagliavo.

Il corso trattava schemi di risoluzione dei problemi e alcuni di essi erano il “2 pointers” (algoritmo a 2 puntatori), lo “sliding window” (algoritmo a finestra scorrevole) e il “frequency counter” (contatore di frequenza). Questi pattern sono molto utili per ottimizzare il codice, evitano l’uso di cicli for annidati e sono più facili da leggere.

Un buon esempio è la verifica di una parola palindroma. Invece di usare due cicli for  annidati (approccio lento), si possono avere due puntatori che inizialmente sono 0 e array.length - 1 (lunghezza dell’array meno 1) e che incrementano a sinistra e diminuiscono a destra dopo 1 ciclo se hanno lo stesso carattere. Questo ottimizza notevolmente il mio codice, perché se usassi l’approccio del ciclo annidato, la mia complessità temporale sarebbe O(n²), molto più lenta di O(n) usando l’approccio dei 2 puntatori.

Dopo aver assimilato il contenuto del corso, ho provato a risolvere alcuni dei problemi proposti da LeetCode e mi sono reso conto di quanto fossi impreparato, incapace di risolvere anche i problemi più semplici. Ho provato a passare ad altri problemi, ma il più delle volte non riuscivo a formulare una soluzione. Ho provato e riprovato, ma per ogni 10 problemi che ho visto, sono riuscito a risolvere solo 2 o 3. Così, sentendomi esausto, mi sono preso una pausa dal DSA.

Un esempio di problema che non sono riuscito a risolvere è stato il LeetCode 1876 dal titolo: “Substring of size 3 with distinct characters”. Si tratta di un classico problema di finestra scorrevole, ma sono riuscito a malapena a capire le condizioni da implementare per far funzionare la finestra.

Ora che ho più esperienza con il DSA, mi sono reso conto che questo problema è stato etichettato come “facile” per un motivo: non c’era alcuna logica ‘extra’ da implementare, a differenza dei problemi intermedi, era solo il vecchio algoritmo. Tutto quello che bisognava fare era impostare due puntatori al primo e al terzo carattere della stringa, spostarsi a sinistra e a destra da lì, incrementare la variabile “risultato” se c’era una corrispondenza e quando il puntatore destro raggiungeva l’ultimo carattere della stringa, controllare se c’era una corrispondenza e infine restituire il risultato.

Gestire DSA tramite LeetCode

Inizialmente mi sono sentito sopraffatto dai problemi del DSA e ho pensato di rinunciare. Tuttavia, approfondendo il materiale del corso, ho capito che la perseveranza e la pratica sono stati fondamentali. Ho iniziato con i problemi più semplici a due puntatori e sono passato gradualmente a sfide più complesse. Nel corso del tempo, ho notato un miglioramento significativo nelle mie capacità e velocità di risoluzione dei problemi.

Mi ha stupito l’evoluzione delle mie capacità di risoluzione dei problemi. Ho affrontato sfide che un tempo sembravano insormontabili, dalle soluzioni di base a due puntatori, a tecniche più avanzate come la finestra scorrevole e il “binary search” (ricerca binaria).

Un problema particolare che vorrei evidenziare è il LeetCode 2824 “Conta le coppie la cui somma è inferiore all’obiettivo”. Inizialmente l’ho affrontato con una soluzione a due puntatori, ma non ero soddisfatto delle prestazioni. Un collega mi ha suggerito di utilizzare la ricerca binaria, che mi ha portato a ottimizzare l’algoritmo ordinando prima l’array. Questo ha migliorato la complessità temporale da O(2 (n log n)) a O(n log n). 

Conclusioni

Il mio viaggio nel DSA non è ancora finito. Sono ansioso di esplorare argomenti più avanzati come gli alberi, gli algoritmi DFS (Depth First Search) e BFS (Breadth First Search) e le implementazioni di ordinamento personalizzate. Anche se non ho applicato direttamente molti di questi concetti nei progetti di sviluppo web, hanno migliorato in modo significativo le mie capacità di risoluzione dei problemi e il mio approccio alla scrittura del codice. Sono diventato più proattivo, creativo e riflessivo nell’affrontare le sfide, adottando una mentalità più difensiva per anticipare potenziali problemi come dati incompleti o errati. Questo ha indubbiamente migliorato la qualità e l’efficienza del mio codice.


Autore: Miguel De Leon, Front-end Developer @Bitrock

Vuoi saperne di più sui nostri servizi? Compila il modulo e fissa un incontro con il nostro team!

Skip to content