Lahendame Google'i intervjuu ĂŒlesande JavaScriptis: 4 erinevat viisi

Lahendame Google'i intervjuu ĂŒlesande JavaScriptis: 4 erinevat viisi

Kui ma uurisin algoritmide jĂ”udlust, leidsin selle video Google'i simulatsioonintervjuudest. See annab mitte ainult ĂŒlevaate sellest, kuidas suured tehnoloogiaettevĂ”tted vestlusi lĂ€biviivad, vaid aitab ka mĂ”ista, kuidas algoritmilisi probleeme lahendada, tehes seda kĂ”ige tĂ”husamal viisil.

See artikkel on omamoodi video kaaslane. Selles annan kommentaare kĂ”igi esitatud lahenduste kohta ning oma versiooni lahendusest JavaScriptis. Arutame ka iga algoritmi nĂŒansse.

Tuletame meelde: kĂ”igile «Habr» lugejatele – 10 000 rubla allahindlus igale Skillboxi kursusele, kasutades sooduskoodi «Habr».

Skillbox soovitab: Praktiline kursus «Mobiilne arendaja PRO».

Ülesande seadmine

Meile antakse jÀrjestatud massiiv ja mÀÀratud vÀÀrtus. SeejÀrel palutakse luua funktsioon, mis tagastab true vÔi false, sÔltuvalt sellest, kas mis tahes kahe massiivi arvu summa vÔib olla vÔrdne mÀÀratud vÀÀrtusega.

TeisisÔnu, kas massiivis on kaks tÀisarvu x ja y, mille summa on antud vÀÀrtus?

NĂ€ide A

Kui meile anti massiiv [1, 2, 4, 9] ja vÀÀrtus 8, tagastab funktsioon false, kuna kaks massiivi numbrit ei saa anda 8 summana.

NĂ€ide B

Kuid kui see on massiiv [1, 2, 4, 4] ja vÀÀrtus 8, peab funktsioon tagastama true, kuna 4 + 4 = 8.

Lahendus 1. Brute Force

Aja keerukus: O(NÂČ).
Ruumiline keerukus: O(1).

KĂ”ige ilmselgem lĂ€henemine on kasutada paari sisemist tsĂŒklit.

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 pidada tÔhusaks, kuna see kontrollib iga vÔimalikku kahe massiivi elemendi summat ja samuti vÔrdleb iga paari indekseid kaks korda. (NÀiteks kipub i = 1 ja j = 2 olema sisuliselt sama, mis vÔrdlemine i = 2 ja j = 1, kuid see lahendus proovib mÔlemat varianti).

Kuna meie lahendus kasutab kahte sisemist tsĂŒklit for, on see ruutmeetriline ajakeerukus O(NÂČ).


Lahendus 2. Binaarne otsing

Aja keerukus: O(Nlog(N)).
Ruumiline keerukus: O(1)
.

Kuna massiivid on jĂ€rjestatud, saame otsida lahendust, kasutades binaarset otsimist. See on kĂ”ige tĂ”husam algoritm jĂ€rjestatud massiivide jaoks. Binaarse otsimise enda tĂ€itmise aeg on O(log(N)). Siiski tuleb kasutada for-tsĂŒklit, et kontrollida iga elementi kĂ”igi muude vÀÀrtuste suhtes.

Nii vÔib vÀlja nÀha lahendus. Et kÔik oleks arusaadav, kasutame eraldi funktsiooni binaarse otsingu kontrollimiseks. Samuti funktsiooni removeIndex (), mis tagastab massiivi versiooni, milles on arvestatud kindlat indeksit.

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 algab indeksist [0]. Siis loob ta versiooni massiivist, millel on vĂ€listatud esimene indeks, ja kasutab binaarset otsingut, et kontrollida, kas saab massiivi ĂŒhe ĂŒlejÀÀnud vÀÀrtusega koos anda soovitud summa. Seda teavet tehakse kord igale massiivi elemendile.

Isegi for-tsĂŒkkel on lineaarse ajakompleksusega O (N), kuid for-tsĂŒkli sees sooritame binaarse otsingu, mis annab kogu ajakompleksuse O (Nlog (N)). See lahendus on parem kui eelmine, kuid veel on ruumi parandamiseks.


Lahendus 3. Liine aeg

Ajakompleksus: O(N).
Ruumiline keerukus: O(1).

NĂŒĂŒd hakkame ĂŒlesannet lahendama, meeles pidades, et massiiv on sorteeritud. Lahendus seisneb kahe arvu vĂ”tmes: ĂŒks alguses ja ĂŒks lĂ”pus. Kui tulemus ei vasta soovitule, siis vahetame algus- ja lĂ”pp-punkti.

LÔpuks kohtume kas soovitud vÀÀrtusega ja tagastame true, vÔi algus- ja lÔpp-punktid satuvad 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 nĂ€ib olevat optimaalne. Kuid kes garanteerib, et massiiv on jĂ€rjestatud?

Mis siis?

Esmapilgul oleksime vĂ”inud kĂ”igepealt lihtsalt massiivi jĂ€rjestada ja siis kasutada ĂŒlaltoodud lahendust. Kuid kuidas see mĂ”jutab tĂ€itmise aega?

Parim algoritm on kiire sorteerimine, mille ajakompleksus on O (Nlog (N)). Kui kasutame seda meie optimaalses lahenduses, siis selle jÔudlus muutub O (N) -lt O (Nlog (N)). Kas on vÔimalik leida lineaarne lahendus jÀrjestamata massiivi puhul?

Lahendus 4

Ajakompleksus: O(N).
Ruumiline komplekssus: O(N).

Jah, lineaarne lahendus eksisteerib, selle jaoks tuleb luua uus massiiv, mis sisaldab loetelu kohtumistest, mida me otsime. Kompromiss siin on aktiivsem mĂ€lu kasutamine: see on ainus lahendus artiklis, mille ruumiline 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 vaadata, kas ĂŒks neist on meie vÀÀrtusega vĂ”rdne. Kui jah, 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 pĂ”hialus on for-tsĂŒkkel, mis, nagu me eespool nĂ€gime, omab lineaarset ajakompleksust O (N).

Meie funktsiooni teine iteratiivne osa on Array.prototype.include(), JavaScripti meetod, mis tagastab true vÔi false, olenevalt sellest, kas massiiv sisaldab antud vÀÀrtust.

Ajakompleksuse vÀlja selgitamiseks Array.prototype.includes() osas saame vaadata MDN-i poolt pakutud polyfill'i (ja JavaScriptis kirjutatud) vÔi kasutada meetodit, mis on kirjutatud JavaScripti mootori lÀhtekoodis, nagu 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() iteratiivne osa sammu 7 while-tsĂŒkkel, mis (peaaegu) katab antud massiivi pikkuse. See tĂ€hendab, et selle ajakompleksus on samuti lineaarne. Kuna see on alati meie peamisse massiivi ĂŒheni sammu vĂ”rra maas, siis ajakompleksus on O (N + (N — 1)). Big O notation'i kasutades lihtsustame selle O (N) — sest just N-l on suurim mĂ”ju sisendi suuruse suurenedes.

Ruumilise keerukuse osas on vajalik lisa massiiv, mille pikkus peegeldab algset massiivi (ĂŒhe vĂ”rra vĂ€hem, jah, kuid seda saab ignoreerida), mis viib ruumilise keerukuseni O (N). Suurenenud mĂ€lu kasutamine tagab aga algoritmi maksimaalse efektiivsuse.


Loodan, et artikkel osutub teile kasulikuks video intervjuu lisana. See nĂ€itab, et lihtne ĂŒlesanne saab lahendada mitmel erineval viisil erineva ressursikasutusega (aeg, mĂ€lu).

Skillbox soovitab:

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster