Deci, te afli. Ușurat. Epuizat. În sfârșit, ați venit cu o abordare pentru a rezolva întrebarea complicată de codare pe care v-a adresat-o intervievatorul. Poate chiar ai scris-o pe tablă, linie cu linie. Și ai făcut timp bun! Ești la doar 20 de minute de întâlnire. Intervievatorul trebuie să fie impresionat.
Dreapta?
"Acest lucru va funcționa, dar aveți idei despre cum să o faceți mai eficient?"
Inima ta se scufundă. Te-ai gândit că ai terminat cu partea complicată a designului algoritmului! Încercați să vă gândiți la mai multe modalități de a rezolva problema, dar tot ce vă puteți gândi este singura abordare cu care v-ați propus deja.
Acest lucru se întâmplă aproape tuturor. Și nu este pentru că sunt proști. Se datorează faptului că majoritatea oamenilor nu au o metodă pentru îmbunătățirea eficienței algoritmilor lor.
Dar adevărul este că sunt destule. Data viitoare când sunteți împiedicat, încercați să aplicați aceste trei abordări comune.
1. Folosiți Hash Harta
Asta e corect. Hărți Hash / tablouri / dicționare asociative (ele poartă mai multe nume, în funcție de limbajul de programare pe care îl utilizați) au o capacitate magică de a reduce timpul de rulare al algoritmilor.
De exemplu, să presupunem că întrebarea era să găsim numărul cel mai repetat într-o serie de numere.
Primul tău gând ar fi să sari în unele bucle. Pentru fiecare dintre numerele noastre, calculați-i numărul și vedeți dacă este cel mai mare. Cum obținem numărul pentru fiecare număr? Buclați-vă prin tablou, numărând de câte ori apare! Vorbim deci despre două bucle cuibărite. În pseudocod:
def get_mode (nums): max_count = 0 mode = nul pentru potential_mode în nums: count = 0 pentru numărul în_array: count + = 1 if count> = max_count: mode = potent_mode max_count = count return mode
Momentan, facem o analiză o dată pentru întregul nostru tablou o dată pentru fiecare element din tablou - dar putem face mai bine. În nota mare O, este timpul total O (n 2 ).
Dacă stocăm numărarea într-o hartă hash (maparea numerelor la numărul lor), putem rezolva problema într-o singură plimbare prin matricea (timp O (n)!):
def get_mode (nums): max_count = 0 mode = null count = HashMap nou, începând fiecare valoare de la 0 pentru potent_mode în nums: count + = 1 if count> max_count: mode = potential_mode max_count = count return mode
Mult mai repede!
2. Folosiți manipularea biților
Chiar asta te va separa de pachet. Nu se aplică oricărei probleme, dar dacă țineți acest lucru în buzunarul din spate și îl eliminați la momentul potrivit, veți arăta ca un rockstar.
Iată un exemplu: Să presupunem că am avut o serie de numere, în care fiecare număr apare de două ori, cu excepția unui număr care apare doar o singură dată. Scriem o funcție pentru a găsi numărul singur, care nu se repetă.
Primul tău instinct ar putea fi să folosești o hartă hash, din moment ce tocmai am vorbit despre asta. Acesta este un bun instinct de a avea! Și va funcționa pentru acesta. Putem face o hartă „similară” foarte similară și o putem folosi pentru a vedea ce număr se încheie cu un număr de 1.
Dar există o cale și mai bună. Dacă sunteți familiarizați cu manipularea biților, este posibil să fiți familiarizați cu XOR. Un lucru deosebit în ceea ce privește XOR este că dacă XOR un număr cu el însuși, biții „anulează” la 0. Pentru această problemă, dacă XOR fiecare număr din tablă împreună, vom rămâne cu un singur număr care nu nu se anulează:
def find_unrepeated (nums): nerepetat = 0 pentru num in nums: nerepetat = nerepetat XOR num return nerepetat
3. Mergeți jos
Scrieți o funcție care emite numărul „al șaptelea” Fibonacci, dat un număr n. Acesta este un clasic și se pretează foarte bine la recursiune:
def fib (n): dacă n este 0 sau 1: return 1 fib retur (n-1) + fib (n-2)
Dar răspunsul simplu recursiv nu este singurul! Gândiți-vă cu atenție ce face această funcție. Să presupunem că n este 5. Pentru a obține răspunsul, apelează recursiv la fib (4) și fib (3). Acum, ce face acest apel la fibră (4)? Se numește fibre (3) și fibre (2). Dar am spus doar că am apelat deja la fibre (3)! Această funcție drăguță recursivă face multă repetare. Costul total al timpului se dovedește a fi O (2 n ). Este rău - mult mai rău decât O (n 2 ).
În loc să trecem de la n în mod recursiv la 1, să mergem „de jos în sus”, de la 1 la n. Acest lucru ne permite să omitem recursiunea:
def fib (n): anterior = 0 anterior_previous = 1 pentru i în intervalul 1 până la n: curent = anterior + anterior_previous precedent_previous = precedent anterior = curent return curent
Codul este mai lung, dar este mult mai eficient! Până la O (n) timp. Ca un bonus suplimentar cu algoritmi recursivi de derulare, economisim spațiu. Toate acele apeluri recursive se acumulează în stiva de apeluri, care stă în memorie și se bazează pe costurile noastre spațiale. Funcția noastră recursivă a avut un cost spațial O (n), dar acesta iterativ ocupă spațiul O (1).
Data viitoare când intervievatorul vă cere să îmbunătățiți eficiența soluției dvs., încercați să parcurgeți aceste strategii și să vedeți dacă acestea vă ajută. Cu o practică suficientă, veți găsi probabil săriți direct la soluția optimizată, sărind soluția mai naivă. Și acesta este un lucru minunat. Nu înseamnă doar că devii un intervievator mai bun, ci înseamnă că devii un inginer mai bun.