Risolve un prublema da una entrevista di Google in JavaScript: 4 modi diffirenti

Risolve un prublema da una entrevista di Google in JavaScript: 4 modi diffirenti

Quandu aghju studiatu u rendiment di l'algoritmi, aghju scontru questu Questu hè un video di una entrevista simulata di Google.. Ùn dà micca solu una idea di cumu si sò realizati l'entrevista in e grandi corporazioni tecnologiche, ma permette ancu di capisce cumu i prublemi algoritmichi sò risolti in modu più efficiente pussibule.

Questu articulu hè un tipu d'accumpagnamentu à u video. In questu aghju furnitu cumenti nantu à tutte e soluzioni mostrate, più a mo propria versione di a suluzione in JavaScript. I sfumaturi di ogni algoritmu sò ancu discututi.

Ramintemu: per tutti i lettori di "Habr" - un scontu di 10 000 rubles quandu si iscrizzione in ogni cursu Skillbox cù u codice promozionale "Habr".

Skillbox consiglia: Corso praticu "Sviluppatore Mobile PRO".

Formulazione di u prublema

Ci hè datu un array urdinatu è un valore specificu. Tandu hè dumandatu à creà una funzione chì torna veru o falsu secondu chì a summa di qualsiasi dui numeri in u array pò uguali à un valore datu.

In altri palori, ci sò dui numeri interi in l'array, x è y, chì quandu aghjunghjenu uguali à u valore specificatu?

Esempiu A

Se ci hè datu un array [1, 2, 4, 9] è u valore hè 8, a funzione torna falsi perchè ùn ci sò dui numeri in u array ponu aghjunghje à 8.

Esempiu B

Ma s'ellu hè un array [1, 2, 4, 4] è u valore hè 8, a funzione deve vultà vera perchè 4 + 4 = 8.

Soluzione 1: Forza bruta

A cumplessità di u tempu: O(N²).
A cumplessità di u spaziu : O (1).

U significatu più evidenti hè di utilizà un paru di loops nidificati.

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;
};

Questa suluzione ùn hè micca efficace perchè verifica ogni somma pussibuli di dui elementi in l'array è paraguna ancu ogni paru d'indici duie volte. (Per esempiu, quandu i = 1 è j = 2 hè in realtà u listessu cum'è paragunà cù i = 2 è j = 1, ma in questa suluzione pruvemu e duie opzioni).

Perchè a nostra suluzione usa un paru di loops nidificati, hè quadratica cù a cumplessità di u tempu O (N²).


Soluzione 2: Ricerca binaria

A cumplessità di u tempu: O(Nlog(N)).
A cumplessità di u spaziu: O (1)
.

Siccomu i matrici sò urdinati, pudemu cercà una suluzione cù a ricerca binaria. Questu hè l'algoritmu più efficau per arrays urdinati. A ricerca binaria stessa hà un tempu di esecuzione di O(log(N)). Tuttavia, avete sempre bisognu di utilizà un for loop per verificà ogni elementu contru tutti l'altri valori.

Eccu ciò chì una suluzione puderia esse. Per fà e cose chjaru, usemu una funzione separata per cuntrullà a ricerca binaria. È ancu a funzione removeIndex(), chì torna a versione di l'array minus l'indici datu.

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;
};

L'algoritmu parte da l'indice [0]. Dopu crea una versione di a matrice escludendu u primu indice è usa a ricerca binaria per vede se qualcunu di i valori rimanenti pò esse aghjuntu à l'array per pruduce a somma desiderata. Questa azione hè realizata una volta per ogni elementu in u array.

U ciclu for stessu averà una cumplessità di u tempu lineale di O(N), ma in u ciclu for realicemu una ricerca binaria, chì dà una cumplessità di tempu generale di O (Nlog(N)). Sta suluzione hè megliu cà a precedente, ma ci hè sempre spaziu per migliurà.


Soluzione 3: U tempu lineale

A cumplessità di u tempu: O(N).
A cumplessità di u spaziu : O (1).

Avà risolvemu u prublema, ricurdendu chì l'array hè ordinatu. A suluzione hè di piglià dui numeri: unu à u principiu è unu à a fine. Se u risultatu differisce da quellu necessariu, cambiate i punti di partenza è di fine.

Eventualmente avemu da scuntrà u valore desideratu è vultà veru, o i punti di iniziu è di fine cunvergeranu è tornanu falsi.

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;
};


Avà tuttu hè bè, a suluzione pare esse ottimali. Ma quale pò assicurà chì a matrice hè stata urdinata?

Chì allora ?

À u primu sguardu, pudemu avè solu urdinatu l'array prima è dopu aduprà a suluzione sopra. Ma cumu affetterà u tempu di esecuzione?

U megliu algoritmu hè quicksort cù a cumplessità di u tempu O (Nlog (N)). Se l'utilicemu in a nostra suluzione ottima, cambià a so prestazione da O (N) à O (Nlog (N)). Hè pussibule di truvà una suluzione lineale cù un array senza ordine?

Soluzione 4

A cumplessità di u tempu: O(N).
Cumplessità spaziale: O(N).

Iè, ci hè una suluzione lineale; per fà questu, avemu bisognu di creà un novu array chì cuntene a lista di partiti chì avemu cercatu. U scambiu quì hè più usu di memoria: hè l'unica suluzione in a carta cù cumplessità spaziale più grande di O (1).

Se u primu valore di un array datu hè 1 è u valore di ricerca hè 8, pudemu aghjunghje u valore 7 à l'array "valori di ricerca".

Allora, cum'è processemu ogni elementu di l'array, pudemu verificà l'array di "valori di ricerca" è vede s'ellu unu di elli hè uguali à u nostru valore. Se sì, torna veru.

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;
};

A basa di a suluzione hè un loop for, chì, cum'è avemu vistu sopra, hà una cumplessità di u tempu lineale di O (N).

A seconda parte iterativa di a nostra funzione hè Array.prototype.include(), un metudu JavaScript chì vulterà veru o falsu secondu chì l'array cuntene u valore datu.

Per capisce a cumplessità di u tempu di Array.prototype.includes(), pudemu fighjà u polyfill furnitu da MDN (è scrittu in JavaScript) o aduprà un metudu in u codice fonte di un mutore JavaScript cum'è 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;
    }
  });
}

Quì a parte iterativa di Array.prototype.include() hè u ciclu while in u passu 7 chì (quasi) traversa tutta a durata di l'array datu. Questu significa chì a so cumplessità di u tempu hè ancu lineale. Ebbè, postu chì hè sempre un passu daretu à u nostru array principale, a cumplessità di u tempu hè O (N + (N - 1)). Utilizendu Big O Notation, simplificàmu à O (N) - perchè hè N chì hà u più grande impattu quandu aumenta a dimensione di input.

In quantu à a cumplessità spaziale, hè necessariu un array supplementu chì a so lunghezza riflette l'array originale (minus one, sì, ma chì pò esse ignoratu), risultatu in O (N) cumplessità spaziale. Ebbè, l'aumentu di l'usu di memoria assicura a massima efficienza di l'algoritmu.


Spergu chì truvate l'articulu utile cum'è supplementu à a vostra entrevista video. Mostra chì un prublema simplice pò esse risoltu in parechje manere diffirenti cù diverse quantità di risorse utilizati (tempu, memoria).

Skillbox consiglia:

Source: www.habr.com

Add a comment