ProblÄmas atrisinÄÅ”ana no Google intervijas JavaScript: 4 dažÄdi veidi
Kad es pÄtÄ«ju algoritmu veiktspÄju, es saskÄros ar to Å is ir Google izspÄles intervijas video.. Tas ne tikai sniedz priekÅ”statu par to, kÄ notiek intervijas lielajÄs tehnoloÄ£iju korporÄcijÄs, bet arÄ« ļauj saprast, kÄ pÄc iespÄjas efektÄ«vÄk tiek risinÄtas algoritmiskÄs problÄmas.
Å is raksts ir sava veida video pavadÄ«jums. TajÄ es sniedzu komentÄrus par visiem parÄdÄ«tajiem risinÄjumiem, kÄ arÄ« savu risinÄjuma versiju JavaScript. Tiek apspriestas arÄ« katra algoritma nianses.
Mums tiek dots sakÄrtots masÄ«vs un noteikta vÄrtÄ«ba. PÄc tam tiek lÅ«gts izveidot funkciju, kas atgriež patiesu vai nepatiesu atkarÄ«bÄ no tÄ, vai jebkuru divu skaitļu summa masÄ«vÄ var bÅ«t vienÄda ar doto vÄrtÄ«bu.
Citiem vÄrdiem sakot, vai masÄ«vÄ ir divi veseli skaitļi, x un y, kas, saskaitot kopÄ, ir vienÄdi ar norÄdÄ«to vÄrtÄ«bu?
PiemÄrs A
Ja mums tiek dots masÄ«vs [1, 2, 4, 9] un vÄrtÄ«ba ir 8, funkcija atgriezÄ«s nepatiesu, jo neviens masÄ«vs nevar pievienot 8.
B piemÄrs
Bet, ja tas ir masÄ«vs [1, 2, 4, 4] un vÄrtÄ«ba ir 8, funkcijai ir jÄatgriež vÄrtÄ«ba True, jo 4 + 4 = 8.
1. risinÄjums: brutÄls spÄks
Laika sarežģītÄ«ba: O(NĀ²).
Telpas sarežģītība: O(1).
AcÄ«mredzamÄkÄ nozÄ«me ir izmantot ligzdotu cilpu pÄri.
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;
};
Å is risinÄjums nav efektÄ«vs, jo pÄrbauda katru iespÄjamo divu elementu summu masÄ«vÄ un arÄ« salÄ«dzina katru indeksu pÄri divas reizes. (PiemÄram, ja i = 1 un j = 2 patiesÄ«bÄ ir tas pats, kas salÄ«dzinÄt ar i = 2 un j = 1, bet Å”ajÄ risinÄjumÄ mÄs izmÄÄ£inÄm abas iespÄjas).
TÄ kÄ mÅ«su risinÄjumÄ tiek izmantots pÄris ligzdotas cilpas, tas ir kvadrÄtveida ar O(NĀ²) laika sarežģītÄ«bu.
2. risinÄjums: binÄrÄ meklÄÅ”ana
Laika sarežģītība: O(Nlog(N)).
Telpas sarežģītība: O(1).
TÄ kÄ masÄ«vi ir sakÄrtoti, mÄs varam meklÄt risinÄjumu, izmantojot binÄro meklÄÅ”anu. Å is ir visefektÄ«vÄkais algoritms sakÄrtotiem masÄ«viem. BinÄrÄs meklÄÅ”anas darbÄ«bas laiks ir O(log(N)). TomÄr jums joprojÄm ir jÄizmanto for cilpa, lai pÄrbaudÄ«tu katru elementu pret visÄm pÄrÄjÄm vÄrtÄ«bÄm.
LÅ«k, kÄ varÄtu izskatÄ«ties risinÄjums. Lai lietas bÅ«tu skaidras, mÄs izmantojam atseviŔķu funkciju, lai kontrolÄtu binÄro meklÄÅ”anu. Un arÄ« funkcija removeIndex(), kas atgriež masÄ«va versiju mÄ«nus doto indeksu.
const findSum = (arr, val) => {
for (let i = 0; i < arr.length; i++){
if (binarySearch(removeIndex(arr, i), val - arr[i])) {
return true;
}
};
return false;
};
const removeIndex = (arr, 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]) {
end = pivot - 1;
} else if (val > arr[pivot]) {
start = pivot + 1;
};
pivot = Math.floor((start + end) / 2);
if (arr[pivot] === val) {
return true;
}
};
return false;
};
Algoritms sÄkas ar indeksu [0]. PÄc tam tiek izveidota masÄ«va versija, izÅemot pirmo indeksu, un tiek izmantota binÄrÄ meklÄÅ”ana, lai noskaidrotu, vai masÄ«vam var pievienot kÄdu no atlikuÅ”ajÄm vÄrtÄ«bÄm, lai iegÅ«tu vÄlamo summu. Å Ä« darbÄ«ba tiek veikta vienu reizi katram masÄ«va elementam.
PaÅ”ai for cilpai bÅ«s lineÄrÄ laika sarežģītÄ«ba O(N), bet for cilpas iekÅ”pusÄ mÄs veicam binÄro meklÄÅ”anu, kas nodroÅ”ina kopÄjo laika sarežģītÄ«bu O(Nlog(N)). Å is risinÄjums ir labÄks par iepriekÅ”Äjo, taÄu vÄl ir ko uzlabot.
3. risinÄjums: lineÄrais laiks
Laika sarežģītība: O(N).
Telpas sarežģītība: O(1).
Tagad mÄs atrisinÄsim problÄmu, atceroties, ka masÄ«vs ir sakÄrtots. RisinÄjums ir Åemt divus skaitļus: vienu sÄkumÄ un vienu beigÄs. Ja rezultÄts atŔķiras no nepiecieÅ”amÄ, mainiet sÄkuma un beigu punktu.
Galu galÄ mÄs vai nu sastapsim vÄlamo vÄrtÄ«bu un atgriezÄ«simies ar patiesu vÄrtÄ«bu, vai arÄ« sÄkuma un beigu punkti saplÅ«dÄ«s un atgriezÄ«sies false.
const findSum = (arr, val) => {
let start = 0;
let end = arr.length - 1;
while (start < end) {
let sum = arr[start] + arr[end];
if (sum > val) {
end -= 1;
} else if (sum < val) {
start += 1;
} else {
return true;
};
};
return false;
};
Tagad viss ir kÄrtÄ«bÄ, risinÄjums Ŕķiet optimÄls. Bet kurÅ” var garantÄt, ka masÄ«vs tika pasÅ«tÄ«ts?
Ko tad?
No pirmÄ acu uzmetiena mÄs varÄjÄm vienkÄrÅ”i pasÅ«tÄ«t masÄ«vu un pÄc tam izmantot iepriekÅ” minÄto risinÄjumu. Bet kÄ tas ietekmÄs izpildes laiku?
LabÄkais algoritms ir ÄtrÄ kÄrtoÅ”ana ar laika sarežģītÄ«bu O (Nlog (N)). Ja mÄs to izmantosim mÅ«su optimÄlajÄ risinÄjumÄ, tas mainÄ«s tÄ veiktspÄju no O(N) uz O(Nlog(N)). Vai ir iespÄjams atrast lineÄru risinÄjumu ar nesakÄrtotu masÄ«vu?
4 risinÄjums
Laika sarežģītība: O(N).
Telpas sarežģītība: O(N).
JÄ, ir lineÄrs risinÄjums; lai to izdarÄ«tu, mums ir jÄizveido jauns masÄ«vs, kurÄ ir ietverts meklÄto atbilstÄ«bas saraksts. Kompromiss Å”eit ir lielÄks atmiÅas lietojums: tas ir vienÄ«gais risinÄjums dokumentÄ, kura telpas sarežģītÄ«ba ir lielÄka par O(1).
Ja dotÄ masÄ«va pirmÄ vÄrtÄ«ba ir 1 un meklÄÅ”anas vÄrtÄ«ba ir 8, mÄs varam pievienot vÄrtÄ«bu 7 masÄ«vam "meklÄÅ”anas vÄrtÄ«bas".
PÄc tam, apstrÄdÄjot katru masÄ«va elementu, mÄs varam pÄrbaudÄ«t āmeklÄÅ”anas vÄrtÄ«buā masÄ«vu un noskaidrot, vai kÄds no tiem ir vienÄds ar mÅ«su vÄrtÄ«bu. Ja jÄ, atgrieziet patieso.
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;
};
RisinÄjuma pamatÄ ir for cilpa, kurai, kÄ redzÄjÄm iepriekÅ”, ir O(N) lineÄrÄ laika sarežģītÄ«ba.
OtrÄ mÅ«su funkcijas iteratÄ«vÄ daļa ir Array.prototype.include(), JavaScript metode, kas atgriezÄ«s patiesu vai nepatiesu atkarÄ«bÄ no tÄ, vai masÄ«vÄ ir norÄdÄ«tÄ vÄrtÄ«ba.
Lai noskaidrotu Array.prototype.includes() laika sarežģītÄ«bu, mÄs varam aplÅ«kot MDN nodroÅ”inÄto polifill (un rakstÄ«ts JavaScript valodÄ) vai izmantot metodi JavaScript dzinÄja avota kodÄ, piemÄram, 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;
}
});
}
Å eit Array.prototype.include() iteratÄ«vÄ daļa ir while cilpa 7. darbÄ«bÄ, kas (gandrÄ«z) ŔķÄrso visu dotÄ masÄ«va garumu. Tas nozÄ«mÄ, ka arÄ« tÄ laika sarežģītÄ«ba ir lineÄra. TÄ kÄ tas vienmÄr ir vienu soli aiz mÅ«su galvenÄ masÄ«va, laika sarežģītÄ«ba ir O (N + (N - 1)). Izmantojot lielo O apzÄ«mÄjumu, mÄs to vienkÄrÅ”ojam lÄ«dz O(N), jo tieÅ”i N ir vislielÄkÄ ietekme, palielinot ievades lielumu.
AttiecÄ«bÄ uz telpisko sarežģītÄ«bu ir nepiecieÅ”ams papildu masÄ«vs, kura garums atspoguļo sÄkotnÄjo masÄ«vu (mÄ«nus viens, jÄ, bet to var ignorÄt), kÄ rezultÄtÄ rodas O(N) telpiskÄ sarežģītÄ«ba. PalielinÄts atmiÅas lietojums nodroÅ”ina maksimÄlu algoritma efektivitÄti.
Ceru, ka raksts jums noderÄs kÄ papildinÄjums video intervijai. Tas parÄda, ka vienkÄrÅ”u problÄmu var atrisinÄt vairÄkos dažÄdos veidos, izmantojot dažÄdus resursu (laika, atmiÅas) apjomu.