
Kui uurisin algoritmide jĂ”udlust, tuli mulle ette video . See annab mitte ainult ĂŒlevaate, kuidas suured tehnoloogiafirmad intervjuude kĂ€iku korraldavad, vaid aitab ka mĂ”ista, kuidas algoritmilisi probleeme lahendada kĂ”ige tĂ”husamalt.
See artikkel on omamoodi nurgakivi videole. Anan selles kommentaare kĂ”igi esitatud lahenduste kohta ja jagan oma versiooni lahendusest JavaScriptis. Samuti kĂ€sitletakse iga algoritmi nĂŒansse.
Tuletame meelde: kĂ”igile «Habra» lugejatele â 10 000 rubla soodustus, kui registreerite end Skillboxi mis tahes kursusele promokoodi «Habr» abil.
Skillbox soovitab: Praktiline kursus .
Ălesande seadmine
Meile antakse jÀrjestatud massiiv ja teatud vÀÀrtus. SeejÀrel palutakse luua funktsioon, mis tagastab true vÔi false, olenevalt sellest, kas mÔne kahe arvu summa massiivist on vÔrdne antud vÀÀrtusega.
TeisisÔnu, kas massiivis on kaks tÀisarvu x ja y, mis koos liites annavad antud vÀÀrtuse?
NĂ€ide A
Kui meile antakse massiiv [1, 2, 4, 9] ja vÀÀrtus 8, tagastab funktsioon false, sest milline kahe arvu summa massiivist ei saavuta 8.
NĂ€ide B
Aga kui see on massiiv [1, 2, 4, 4] ja vÀÀrtus 8, siis peaks funktsioon tagastama true, kuna 4 + 4 = 8.
Lahendus 1. Brute force
Aja keerukus: O(NÂČ).
Ruumiinne keerukus: O(1).
KĂ”ige ilmsem tĂ€hendus on kasutamine vĂ€heste pesatud tsĂŒklite paaride abil.
const findSum = (arr, val) => {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
if (i !== j && arr[i] + arr[j] === val) {
return true;
};
};
};
return false;
};Seda lahendust ei saa nimetada efektiivseks, kuna see kontrollib iga vĂ”imalikku kahe elementi summat massiivis ja vĂ”rreldes iga indeksipaari kaks korda. (NĂ€iteks, kui i = 1 ja j = 2 â see on tegelikult sama, mis vĂ”rrelda i = 2 ja j = 1, kuid selles lahenduses proovime mĂ”lemat varianti).
Kuna meie lahendus kasutab vĂ€heste pesatud for-tsĂŒkleid, on see ruutjĂ”udlusega ajakompleksus O (NÂČ).
Lahendus 2. Binaarne otsing
Ajakompleksus: O(Nlog(N)).
Ruumiinne keerukus: O(1).
Kuna massiivid on jĂ€rjestatud, saame otsida lahendust kasutades binaarset otsingut. See on kĂ”ige tĂ”husam algoritm jĂ€rjestatud massiivide puhul. Binaarse otsingu enda aeg on O (log (N)). Siiski on ikkagi vajalik kasutada for-tsĂŒklit, et kontrollida iga elementi ja kĂ”iki teisi vÀÀrtusi.
Nii vÔib vÀlja nÀha lahendus. Et kÔik oleks selge, kasutame eraldi funktsiooni binaarse otsingu kontrollimiseks. Ja ka funktsiooni removeIndex(), mis tagastab massiivi versiooni, millest on arvesse vÔetud antud indeks.
const findSum = (arr, val) => {
for (let i = 0; i {
return arr.slice(0, i).concat(arr.slice(i + 1, arr.length));
};
const binarySearch = (arr, val) => {
let start = 0;
let end = arr.length - 1;
let pivot = Math.floor(arr.length / 2);
while (start < end) {
if (val arr[pivot]) {
start = pivot + 1;
};
pivot = Math.floor((start + end) / 2);
if (arr[pivot] === val) {
return true;
}
};
return false;
};Algoritm alustab indeksist [0]. SeejĂ€rel loob ta massiivi versiooni, jĂ€ttes vĂ€lja esimese indeksi, ja kasutab binaarset otsingut, et kontrollida, kas mĂ”ne ĂŒlejÀÀnud vÀÀrtuse lisamine massiivi annaks soovitud summa. See toiming viiakse lĂ€bi ĂŒhe korra iga massiivi elemendi jaoks.
Iseseisvalt on for-tsĂŒkli ajas keerukus O (N), kuid for-tsĂŒkli sees teeme me binaarset otsingut, mis annab koguni ajas keerukuse O (Nlog (N)). See lahendus on parem kui eelmine, kuid veel on, mida tĂ€iustada.
Lahendus 3. Lineaarne aeg
Ajas keerukus: O(N).
Ruumiinne keerukus: O(1).
NĂŒĂŒd lahendame ĂŒlesande, pidades meeles, et massiiv on jĂ€rjestatud. Lahendus seisneb kahe arvu vĂ”tmisest: ĂŒks alguses ja ĂŒks lĂ”pus. Kui summa ei vasta nĂ”utud vÀÀrtusele, muutame algus- ja lĂ”pp-punkti.
LÔppkokkuvÔttes kas kohtame soovitud vÀÀrtust ja tagastame true vÔi algus- ja lÔpp-punktid jÔuavad kokku ja tagastame false.
const findSum = (arr, val) => {
let start = 0;
let end = arr.length - 1;
while (start val) {
end -= 1;
} else if (sum < val) {
start += 1;
} else {
return true;
};
};
return false;
};NĂŒĂŒd on kĂ”ik hĂ€sti, lahendus tundub olevat optimaalne. Aga kes annab garantii, et massiiv oli jĂ€rjestatud?
Mis siis?
Esmapilgul oleksime vÔinud kÔigepealt lihtsalt jÀrjestada massiivi ja seejÀrel kasutada eelmist lahendust. Kuid kuidas see mÔjutaks tÀitmisaega?
Parim algoritm on kiire sorteerimine, mille ajakulu on O(N log(N)). Kui kasutame seda meie optimaalses lahenduses, muutub selle jÔudlus O(N) -lt O(N log(N)) -le. Kas on vÔimalik leida lineaarne lahendus sortimata massiivi jaoks?
Lahendus 4
Ajas keerukus: O(N).
Ruumi keerukus: O(N).
Jah, lineaarne lahendus eksisteerib, selleks tuleb luua uus massiiv, mis sisaldab otsitavate vasteid. Kompromiss on siin suurema mĂ€lu kasutamine: see on ainus lahendus, mille ruumi keerukus ĂŒletab O(1).
Kui selle massiivi esimene vÀÀrtus on 1 ja otsitav vÀÀrtus on 8, saame lisada vÀÀrtuse 7 âotsinguvÀÀrtusteâ massiivi.
SeejĂ€rel, töötades lĂ€bi iga massiivi elemendi, saame kontrollida âotsinguvÀÀrtusteâ massiivi ja nĂ€ha, kas ĂŒks neist on meie vÀÀrtusega. Kui jah, siis tagastame true.
const findSum = (arr, val) => {
let searchValues = [val - arr[0]];
for (let i = 1; i < arr.length; i++) {
let searchVal = val - arr[i];
if (searchValues.includes(arr[i])) {
return true;
} else {
searchValues.push(searchVal);
}
};
return false;
};Lahenduse alus on for tsĂŒkkel, mille ajakulu on, nagu nĂ€gime, lineaarne O(N).
Teine iteratsiooniosa meie funktsioonist on Array.prototype.include(), JavaScripti meetod, mis tagastab true vÔi false sÔltuvalt sellest, kas massiiv sisaldab antud vÀÀrtust.
Kuna me tahame vÀlja selgitada Array.prototype.includes() ajakompleksus, saame vaadata MDN-i pakutud polyfilli (kirjutatud JavaScriptis) vÔi kasutada meetodit JavaScripti mootori lÀhtekoodis, nÀiteks Google V8 (C++).
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(valueToFind, fromIndex) {
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0) {
return false;
}
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
var n = fromIndex | 0;
// 5. If n â„ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
function sameValueZero(x, y) {
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
}
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(valueToFind, elementK) is true, return true.
if (sameValueZero(o[k], valueToFind)) {
return true;
}
// c. Increase k by 1.
k++;
}
// 8. Return false
return false;
}
});
}Siin on Array.prototype.include() iteratsiooniosa while-tsĂŒkkel samm 7, mis (peaaegu) katab antud massiivi pikkuse. See tĂ€hendab, et selle ajakompleksus on samuti lineaarne. Kuna see on alati meie pĂ”himassiivist ĂŒhe sammu tagapool, siis ajakompleksus on O(N + (N - 1)). Kasutades Big O Notationit, lihtsustame selle O(N) â sest just N mĂ”jutab kĂ”ige rohkem sisendi suuruse suurenemise korral.
Kui rÀÀkida ruumilisest keerukusest, siis on vajalik tĂ€iendav massiiv, mille pikkus peegeldab algset massiivi (kuigi see on miinus ĂŒhe vĂ”rra, aga seda saab ignoreerida), mis toob kaasa ruumilise keerukuse O(N). Suurenenud mĂ€lu kasutamine tagab algoritmi maksimaalse efektiivsuse.
Loodan, et artikkel osutub teile kasulikuks lisandina videointervjuule. See nĂ€itab, kuidas lihtne ĂŒlesanne vĂ”ib olla lahendatud mitmel erineval viisil, kasutades erinevaid ressursse (aeg, mĂ€lu).
Skillbox soovitab:
- Rakenduslik veebikursus .
- Veebikursus .
- Praktiline aastakursus .
Allikas: habr.com
