Dus daar ben je. Opgelucht. Uitgeput. Je hebt eindelijk een aanpak bedacht om de lastige codeervraag op te lossen die je interviewer je stelt. Misschien heb je het zelfs regel voor regel op het whiteboard geschreven. En je hebt goede tijd gemaakt! U bent slechts 20 minuten in de vergadering. Je interviewer moet onder de indruk zijn.
Rechts?
"Dit zal werken, maar ideeën om het efficiënter te doen?"
Je hart zinkt. Je dacht dat je klaar was met het lastige algoritme-ontwerpgedeelte! Je probeert meer manieren te bedenken om het probleem op te lossen, maar het enige dat je kunt bedenken is de enige aanpak die je al hebt bedacht.
Dit gebeurt bijna iedereen. En het is niet omdat ze dom zijn. Dit komt omdat de meeste mensen geen methode hebben om de efficiëntie van hun algoritmen te verbeteren.
Maar de waarheid is dat er genoeg zijn. Probeer de volgende keer dat je je stunt, deze drie veel voorkomende benaderingen toe te passen.
1. Gebruik een hashkaart
Dat is juist. Hash-kaarten / associatieve arrays / woordenboeken (ze hebben veel namen, afhankelijk van de programmeertaal die u gebruikt) hebben een magische mogelijkheid om de runtime van algoritmen te verkorten.
Stel bijvoorbeeld dat de vraag was om het meest herhaalde getal in een reeks getallen te vinden.
Je eerste gedachte zou kunnen zijn om in een aantal lussen te springen. Bepaal voor elk van onze nummers de telling en kijk of het de grootste is. Hoe krijgen we de telling voor elk nummer? Loop door de array en tel hoe vaak het voorkomt! Dus we hebben het over twee geneste lussen. In pseudocode:
def get_mode (nums): max_count = 0 mode = null voor potential_mode in nums: count = 0 voor nummer in our_array: count + = 1 if count> = max_count: mode = potential_mode max_count = count return mode
Op dit moment doorlopen we onze hele array eenmaal voor elk item in de array, maar we kunnen het beter doen. In grote O-notatie is dat in totaal O (n 2 ) tijd.
Als we onze tellingen opslaan in een hashkaart (getallen aan hun tellingen toewijzen), kunnen we het probleem oplossen in slechts één wandeling door de reeks (O (n) tijd!):
def get_mode (nums): max_count = 0 mode = null counts = new HashMap, beginnend met elke waarde bij 0 voor potential_mode in nums: counts + = 1 if counts> max_count: mode = potential_mode max_count = counts return mode
Veel sneller!
2. Gebruik bitmanipulatie
Dit zal je echt onderscheiden van de rest. Het is niet van toepassing op elk probleem, maar als je dit in je achterzak houdt en het op het juiste moment kapot maakt, zie je eruit als een rockster.
Hier is een voorbeeld: stel dat we een reeks getallen hadden, waarbij elk nummer twee keer voorkomt, behalve één nummer dat maar één keer voorkomt. We schrijven een functie om het eenzame, niet-herhaalde nummer te vinden.
Je eerste instinct is misschien om een hash-kaart te gebruiken, omdat we er net over hebben gesproken. Dat is een goed instinct om te hebben! En het zal voor deze werken. We kunnen een zeer vergelijkbare 'tellingen'-kaart maken en die gebruiken om te zien welk nummer eindigt met een telling van 1.
Maar er is een nog betere manier. Als u bekend bent met bitmanipulatie, bent u misschien bekend met XOR. Een ding dat speciaal is aan XOR is dat als je een nummer XOR met zichzelf, de bits "opheffen" tot 0. Voor dit probleem, als we elk nummer in de array XOR maken, blijven we achter met het nummer dat niet niet annuleren:
def find_unrepeated (nums): unrepeated = 0 voor num in nums: unrepeated = unrepeated XOR num return unrepeated
3. Ga van onder naar boven
Schrijf een functie die het "nde" Fibonacci-nummer uitvoert, gegeven een nummer n. Deze is een klassieker en leent zich heel goed voor recursie:
def fib (n): als n 0 of 1 is: retour 1 retourfib (n-1) + fib (n-2)
Maar het eenvoudige recursieve antwoord is niet het enige! Denk goed na over wat deze functie doet. Stel dat n 5 is. Om het antwoord te krijgen, wordt recursief fib (4) en fib (3) genoemd. Wat doet dat aan fib (4)? Het noemt fib (3) en fib (2). Maar we zeiden net dat we al een oproep tot fib hadden (3)! Deze schattige recursieve functie doet veel herhalingswerk. De totale tijdskosten blijken O (2 n ) te zijn. Dat is slecht - veel erger dan O (n 2 ).
In plaats van recursief van n naar 1 te gaan, gaan we "bottom-up" van 1 naar n. Hiermee kunnen we de recursie overslaan:
def fib (n): vorige = 0 vorige_previous = 1 voor i in het bereik van 1 tot n: huidig = vorige + vorige_previous vorige_previous = vorige vorige = huidige retourstroom
De code is langer, maar het is veel efficiënter! Tot O (n) tijd. Als een toegevoegde bonus met afrollende recursieve algoritmen, besparen we ruimte. Al die recursieve oproepen worden opgebouwd in de oproepstapel, die in het geheugen zit en meetelt voor onze ruimtekosten. Onze recursieve functie had O (n) ruimtekosten, maar deze iteratieve neemt O (1) ruimte in beslag.
Probeer de volgende keer dat uw interviewer u vraagt om de efficiëntie van uw oplossing te verbeteren, deze strategieën te doorlopen en te kijken of ze helpen. Met voldoende oefening zult u waarschijnlijk merken dat u meteen naar de geoptimaliseerde oplossing springt en de meer naïeve oplossing overslaat. En dat is geweldig. Het betekent niet alleen dat je een betere interviewer wordt, het betekent ook dat je een betere ingenieur wordt.




