JavaScript | LeetCode – Telegram
JavaScript | LeetCode
9.44K subscribers
228 photos
1.14K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.me/+T0COHtFzCJkwMDUy
Вопросы собесов t.me/+kXKgJEjRUww3N2Ni
Вакансии t.me/+CgCAzIyGHHg0Nzky
Download Telegram
Channel created
Задача: №16. 3Sum Closest #medium

Условие:
Учитывая целочисленный массив nums длины n и целочисленную цель, найдите три целых числа в nums, сумма которых наиболее близка к цели. Возвращает сумму трех целых чисел. Вы можете предположить, что каждый вход будет иметь ровно одно решение.


Решение:
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var threeSumClosest = function (nums, target) {
nums.sort((a, b) => a - b);
let ans = 1 << 30;
const n = nums.length;
for (let i = 0; i < n; ++i) {
let j = i + 1;
let k = n - 1;
while (j < k) {
const t = nums[i] + nums[j] + nums[k];
if (t === target) {
return t;
}
if (Math.abs(t - target) < Math.abs(ans - target)) {
ans = t;
}
if (t > target) {
--k;
} else {
++j;
}
}
}
return ans;
};


Пояснение:
Сначала мы сортируем массив, а затем просматриваем его. Для каждого элемента nums[i], мы используем указатели j и k
указать на n+1
и n - 1 соответственно, вычислите сумму трех чисел. Если сумма трех чисел равна целевому значению, мы напрямую возвращаем цель. В противном случае мы обновляем ответ в зависимости от отличия от целевого.
Если сумма трех чисел больше целевого, мы перемещаем k
на одну позицию влево, иначе мы перемещаем j одно место вправо.
Временная сложность равна O(n^2), а пространственная сложность равна O(log n).
Вот, n длина массива.
👍5🤯3🔥2
Задача: №17. Letter Combinations of a Phone Number #medium

Условие:
Учитывая строку, содержащую цифры от 2 до 9 включительно, верните все возможные комбинации букв, которые может представлять число. Верните ответ в любом порядке. Соответствие цифр буквам (как на кнопках телефона) приведено ниже. Обратите внимание, что 1 не соответствует никаким буквам.


Решение:
/**
* @param {string} digits
* @return {string[]}
*/
var letterCombinations = function (digits) {
if (digits.length == 0) {
return [];
}
const ans = [''];
const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz'];
for (const i of digits) {
const s = d[parseInt(i) - 2];
const t = [];
for (const a of ans) {
for (const b of s) {
t.push(a + b);
}
}
ans.splice(0, ans.length, ...t);
}
return ans;
};


Пояснение:
Во-первых, мы используем массив или хеш-таблицу для хранения букв, соответствующих каждой цифре. Затем мы проходим каждую цифру, объединяем соответствующие ей буквы с предыдущими результатами, чтобы получить новые результаты. Временная сложность O(4^n), а пространственная сложность равна O(4^n). Вот, n длина входных цифр.
👍1
Задача: №18. 4Sum #medium

Условие:
Учитывая массив nums из n целых чисел, верните массив всех уникальных четверок [nums[a], nums[b], nums[c], nums[d]] таких, что:
0 <= a, b, c, d < n
a, b, c и d различны.
nums[a] + nums[b] + nums[c] + nums[d] == цель
Вы можете вернуть ответ в любом порядке.



Решение:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[][]}
*/
var fourSum = function (nums, target) {
const n = nums.length;
const ans = [];
if (n < 4) {
return ans;
}
nums.sort((a, b) => a - b);
for (let i = 0; i < n - 3; ++i) {
if (i > 0 && nums[i] === nums[i - 1]) {
continue;
}
for (let j = i + 1; j < n - 2; ++j) {
if (j > i + 1 && nums[j] === nums[j - 1]) {
continue;
}
let [k, l] = [j + 1, n - 1];
while (k < l) {
const x = nums[i] + nums[j] + nums[k] + nums[l];
if (x < target) {
++k;
} else if (x > target) {
--l;
} else {
ans.push([nums[i], nums[j], nums[k++], nums[l--]]);
while (k < l && nums[k] === nums[k - 1]) {
++k;
}
while (k < l && nums[l] === nums[l + 1]) {
--l;
}
}
}
}
}
return ans;
};


Пояснение:
Учитывая массив nums из n целых чисел, верните массив всех уникальных четверок [nums [a], nums [b], nums [c], nums [d]], таких, что:
• 0 = a, b, c, d < n
• a, b, c и d различны.
• цифры[a] + цифры[b] + цифры[c] + цифры[d] == цель
Вы можете вернуть ответ в любом порядке.
2😁1🤔1
Задача: №19. Remove Nth Node From End of List #medium

Условие:
Учитывая заголовок связанного списка, удалите n-й узел из конца списка и верните его заголовок.


Решение:

/
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function (head, n) {
const dummy = new ListNode(0, head);
let fast = dummy,
slow = dummy;
while (n--) {
fast = fast.next;
}
while (fast.next) {
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return dummy.next;
};


Пояснение:
Мы определяем два указателя, быстрый и медленный,
оба из которых изначально указывают на фиктивный головной
узел связанного списка.
Затем сначала быстрый указатель перемещается вперед на n
шагов, затем быстрый и медленный указатели
перемещаются вперед вместе, пока быстрый
указатель не достигнет конца связанного списка. На
данный момент узел, на который указывает
slow. next, является предшественником n-го
узла с конца, и мы можем удалить его.
Временная сложность равна O(n), где n -
длина связанного списка. Сложность пространства
равна O(1).
1
Задача: №20. Valid Parentheses #easy

Условие:
Учитывая строку s, содержащую только символы '(', ')', '{', '}', '[' и ']', определите, является ли входная строка допустимой. Входная строка действительна, если: Открытые скобки должны закрываться скобками того же типа.
Открытые скобки должны закрываться в правильном порядке.
Каждой закрывающей скобке соответствует открытая скобка того же типа.


Решение:

/**
* @param {string} s
* @return {boolean}
*/
var isValid = function (s) {
let stk = [];
for (const c of s) {
if (c == '(' c == '{' c == '[') {
stk.push(c);
} else if (stk.length == 0 !match(stk[stk.length - 1], c)) {
return false;
} else {
stk.pop();
}
}
return stk.length == 0;
};

function match(l, r) {
return (l == '(' && r == ')') (l == '[' && r == ']') || (l == '{' && r == '}');
}


Пояснение:
Пройдите по строке скобок s. При
обнаружении левой скобки вставьте
текущую левую скобку в стек; при
обнаружении правой скобки извлеките верхний
элемент из стека (если стек пуст,
сразу верните значение false ) и оцените
, соответствует ли оно. Если оно не совпадает, напрямую
верните значение false.
В качестве альтернативы, при обнаружении левой
скобки вы можете поместить соответствующую
правую скобку в стек; при
обнаружении правой скобки извлеките верхний
элемент стека (если стек пуст,
непосредственно верните значение false ) и оцените, равны ли
они. Если они не совпадают,
напрямую верните значение false.

Разница между этими двумя
методами заключается только во времени
преобразования скобок: один - при вставке в стек
, а другой - при
извлечении из стека.

В конце обхода, если стек
пуст, это означает, что строка в скобках верна,
возвращает значение true; в противном случае возвращает значение false.
Временная сложность равна O(n), а
пространственная сложность равна O(n). Здесь n - это
длина строки скобок s.
👍2
Задача: №21. Merge Two Sorted Lists #easy

Условие:
Вам даны заголовки двух отсортированных связанных списков list1 и list2. Объедините два списка в один отсортированный список. Список должен быть составлен путем сращивания узлов первых двух списков. Возвращает заголовок объединенного связанного списка.


Решение:

/
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/
* @param {ListNode} list1
* @param {ListNode} list2
* @return {ListNode}
*/
var mergeTwoLists = function (list1, list2) {
if (!list1 !list2) {
return list1 list2;
}
if (list1.val <= list2.val) {
list1.next = mergeTwoLists(list1.next, list2);
return list1;
} else {
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
};


Пояснение:
Сначала мы оцениваем, являются ли связанные списки l1
и l2 пустыми. Если один из них пуст,
мы возвращаем другой связанный список. В противном случае
мы сравниваем головные узлы l1 и l2:
• Если значение головного узла из ₁
меньше или равно значению
головного узла из l2, мы рекурсивно вызываем
функцию mergeTwoLists (l1. далее, l2)
и соединяем головной узел из ₁ с
возвращаемым головным узлом связанного списка и
возвращаем головной узел из l1.
• В противном случае мы рекурсивно вызываем
функцию mergeTwoLists (l1, l2. next)
и соединяем головной узел l2 с
возвращаемым головным узлом связанного списка и
возвращаем головной узел l2.
Временная сложность равна O(m + n), а
пространственная - O(m + n). Здесь m
и n - длины двух связанных списков
соответственно.
🔥3🤮1
Задача: №22. Generate Parentheses #medium

Условие:
Учитывая n пар круглых скобок, напишите функцию, которая генерирует все комбинации правильных круглых скобок.
Решение:
/**
* @param {number} n
* @return {string[]}
*/
var generateParenthesis = function (n) {
function dfs(l, r, t) {
if (l > n r > n l < r) {
return;
}
if (l == n && r == n) {
ans.push(t);
return;
}
dfs(l + 1, r, t + '(');
dfs(l, r + 1, t + ')');
}
let ans = [];
dfs(0, 0, '');
return ans;
};


Пояснение:
Диапазон значений n в задаче равен [1, 8], поэтому
мы можем напрямую решить эту проблему с помощью
"поиска методом перебора + обрезки".
Мы создаем функцию dfs(l,r,t), где I
и представляют количество левых и
правых скобок соответственно, а t
представляет текущую последовательность скобок.
Тогда мы можем получить следующую рекурсивную
структуру:
• Если > n или r > n или l<r, то
текущая комбинация скобок t
недопустима, возвращайте напрямую;
• Если l = n и r = n, то текущая
комбинация скобок t допустима, добавьте ее в
массив ответов ans и возвращайте
напрямую;
• Мы можем добавить левую скобку
и рекурсивно выполнить dfs (1 + 1,
r, t + "(")). ;
• Мы также можем добавить правую
скобку и рекурсивно выполнить
dfs(1, r +1, t + ")").
Временная сложность равна O(2×2 × n), а
пространственная - O(n).
🔥2🤮2
Channel name was changed to «JavaScript | LeetCode»
Задача: №23. Merge k Sorted Lists #hard

Условие:
Вам дан массив из k списков связанных списков, каждый связанный список отсортирован в порядке возрастания. Объедините все связанные списки в один отсортированный связанный список и верните его.


Решение:

/
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/
* @param {ListNode[]} lists
* @return {ListNode}
*/
var mergeKLists = function (lists) {
const pq = new MinPriorityQueue({ priority: node => node.val });
for (const head of lists) {
if (head) {
pq.enqueue(head);
}
}
const dummy = new ListNode();
let cur = dummy;
while (!pq.isEmpty()) {
const node = pq.dequeue().element;
cur.next = node;
cur = cur.next;
if (node.next) {
pq.enqueue(node.next);
}
}
return dummy.next;
};


Пояснение:
Диапазон значений n в задаче равен [1, 8], поэтому
мы можем напрямую решить эту проблему с помощью
"поиска методом перебора + обрезки".
Мы создаем функцию dfs(l,r,t), где I
и представляют количество левых и
правых скобок соответственно, а t
представляет текущую последовательность скобок.
Тогда мы можем получить следующую рекурсивную
структуру:
• Если > n или r > n или l<r, то
текущая комбинация скобок t
недопустима, возвращайте напрямую;
• Если l = n и r = n, то текущая
комбинация скобок t допустима, добавьте ее в
массив ответов ans и возвращайте
напрямую;
• Мы можем добавить левую скобку
и рекурсивно выполнить dfs (1 + 1,
r, t + "(")). ;
• Мы также можем добавить правую
скобку и рекурсивно выполнить
dfs(1, r +1, t + ")").
Временная сложность равна O(2×2 × n), а
пространственная - O(n).
👍1
Задача: №24. Swap Nodes in Pairs #medium

Условие:
Учитывая связанный список, поменяйте местами каждые два соседних узла и верните его голову. Вы должны решить проблему, не изменяя значения в узлах списка (т. е. изменять можно только сами узлы).


Решение:

/
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/
* @param {ListNode} head
* @return {ListNode}
*/
var swapPairs = function (head) {
if (!head || !head.next) {
return head;
}
const t = swapPairs(head.next.next);
const p = head.next;
p.next = head;
head.next = t;
return p;
};


Пояснение:
Мы можем реализовать замену двух узлов в связанном списке с помощью рекурсии.
Условием завершения рекурсии является отсутствие узлов в связанном списке или наличие только одного узла в связанном списке. В данный момент замена не может быть выполнена, поэтому мы возвращаем этот узел напрямую.
В противном случае мы рекурсивно меняем заголовок связанного списка местами. следующий. далее, и пусть замененный головной узел будет t. Затем мы позволяем p быть следующим узлом head, и пусть p указывает на head, а head указывает на t, и, наконец, возвращаем p.
Временная сложность равна O(n), а пространственная - O(n). Здесь n - длина связанного списка.
👍1
Задача: №26.Remove Duplicates from Sorted Array #easy

Условие:
При наличии целочисленного массива nums, отсортированного в порядке неубывания, удалите дубликаты на месте, чтобы каждый уникальный элемент отображался только один раз. Относительный порядок элементов должен оставаться неизменным. Затем верните количество уникальных элементов в nums. Предположим, что количество уникальных элементов nums равно k, для того, чтобы их приняли, вам нужно выполнить следующие действия: Измените массив nums таким образом, чтобы первые k элементов nums содержали уникальные элементы в том порядке, в котором они изначально присутствовали в nums. Остальные элементы nums не важны, так же как и размер nums.
Верните k.


Решение:
/**
* @param {number[]} nums
* @return {number}
*/
var removeDuplicates = function (nums) {
let k = 0;
for (const x of nums) {
if (k === 0 || x !== nums[k - 1]) {
nums[k++] = x;
}
}
return k;
};


Пояснение:
Мы используем переменную k для записи текущей длины обрабатываемого массива. Изначально
значение k = 0 представляет пустой массив.
Затем мы проходим по массиву слева направо. Для каждого элемента x, с которым мы сталкиваемся, если
k = 0 или x != nums|k - 1], мы помещаем x в позицию nums|k|, а затем увеличиваем k на 1. В противном случае значение x совпадает с значением nums|k - 1], поэтому мы пропускаем этот элемент.
Продолжайте обход, пока не будет пройден весь массив.
Таким образом, когда обход завершается, первые k элементов в числах являются ответом, который мы ищем, а k - это длина ответа.
Временная сложность равна O(n), а пространственная сложность равна O(1). Здесь n - это длина массива.
Дополнение:
Исходная задача требует, чтобы одно и то же число появлялось не более одного раза. Мы можем расширить ее, чтобы сохранить не более k одинаковых чисел.
Поскольку одно и то же число может сохраняться не более k раз, мы можем напрямую сохранить первые k элементов исходного массива;
Для следующих чисел предпосылка их сохранения такова: текущее число x сравнивается с последним k-м элементом из ранее сохраненных чисел. Если они отличаются друг от друга, сохраните их, в противном случае пропустите.
👍4🤯2
Задача: №27.Remove Element #easy

Условие:
Учитывая целочисленный массив nums и целое значение val, удалите все вхождения val в nums на месте. Порядок элементов может быть изменен. Затем верните количество элементов в nums, которые не равны val. Учтите, что количество элементов в nums, которые не равны val, равно k, чтобы их приняли, вам нужно выполнить следующие действия: Измените массив nums таким образом, чтобы первые k элементов в nums содержали элементы, которые не равны val. Остальные элементы nums не важны, так же как и размер nums. Верните k.

Решение:
/**
* @param {number[]} nums
* @param {number} val
* @return {number}
*/
var removeElement = function (nums, val) {
let k = 0;
for (const x of nums) {
if (x !== val) {
nums[k++] = x;
}
}
return k;
};


Пояснение:
Мы используем переменную k для записи количества элементов, которые не равны val.
Проходим по массиву nums, если текущий элемент x не равен val, затем присваиваем x значение nums|k) и увеличиваем k на 1.
Наконец, возвращаем k.
Временная сложность равна O(n), а пространственная - O(1), где n - длина массива nums
.
2
Задача: 28. Find the Index of the First Occurrence in a String #easy

Условие:
Учитывая две строки, игла и стог сена, верните индекс первого вхождения иглы в стоге сена или -1, если игла не является частью стога сена.



Решение:
function strStr(haystack, needle) {  if (needle === "") {
return 0; }
for (let i = 0; i < haystack.length; i++) {
let haySlice = haystack.slice(i, i + needle.length); if (haySlice === needle) {
return i; }
} return -1;
}

Пояснение:
1. Функция strStr принимает две строки: haystack - строку, в которой нужно найти подстроку, и needle - подстроку, которую нужно найти.
2. Сначала функция проверяет, если needle пустая строка, то возвращает 0, так как подстрока пустая и она есть в любой строке на любой позиции.
3. Далее функция использует цикл for, чтобы пройтись по каждому символу в строке haystack.
4. На каждой итерации цикла, функция создает подстроку haySlice, начиная с текущей позиции i и длиной подстроки needle.
5. Затем происходит сравнение подстроки haySlice с needle. Если они равны, функция возвращает индекс i, то есть позицию, с которой начинается найденная подстрока needle в строке haystack.
6. Если подстрока haySlice не равна needle, цикл продолжает итерироваться через строку haystack.
7. Если не удалось найти подстроку needle в строке haystack, функция возвращает -1, указывая на отсутствие искомой подстроки в строке.
4
Задача: 29. Divide Two Integers #medium

Условие:
Учитывая два целых числа: делимое и делитель, разделите два целых числа, не используя операторы умножения, деления и модификатора.
Целочисленное деление должно сокращаться до нуля, что означает потерю дробной части. Например, 8,345 будет сокращено до 8, а -2,7335 будет сокращено до -2.
Возвращает частное после деления делимого на делитель.
Примечание. Предположим, мы имеем дело со средой, которая может хранить целые числа только в пределах 32-битного целого диапазона со знаком: [−2^31, 2^31 − 1]. В этой задаче, если частное строго больше 2^31 - 1, верните 2^31 - 1, а если частное строго меньше -2^31, верните -2^31.


Решение:
var divide = function(dividend, divisor) {    if(dividend===0) return 0;
let signal = 1; if(dividend<0){
signal *= -1; }
if(divisor<0){ signal *= -1;
} dividend = Math.abs(dividend);
divisor = Math.abs(divisor);
let result = help(dividend, divisor)*signal; if(result>Math.pow(2,31)-1){
result = Math.pow(2,31)-1; }else if(result<-1*Math.pow(2,31)){
result = Math.pow(2,31)*(-1); }
return result;
function help(dividend, divisor){ if(dividend < divisor) return 0;
let res = 0; let origDivisor = divisor;
let prev = 0; while(dividend>=divisor){
if(res===0){ res += 1;
}else{ res += res;
} prev = divisor;
divisor += divisor; }
dividend -= prev; res += help(dividend, origDivisor);
return res; }
};

Пояснение:
1. Функция divide определяется как анонимная функция, принимающая два аргумента dividend и divisor.
2. Проверяется условие, если dividend равен 0, то сразу возвращается 0, так как деление на ноль равно 0.
3. Создается переменная signal со значением 1, которая будет использоваться для определения знака результата.
4. Если dividend меньше 0, знак signal умножается на -1, чтобы сохранить отрицательный результат.
5. Аналогично, если divisor меньше 0, знак signal также умножается на -1.
6. Затем модулируются dividend и divisor, чтобы оба значения стали положительными.
7. В переменной result сохраняется результат вызова вспомогательной функции help с аргументами dividend и divisor, умноженный на знак signal.
8. После вычислений результата проверяется его на соответствие диапазону значений для 32-битного целого числа, и при необходимости результат обрезается до максимальных или минимальных значений.
9. Возвращается итоговый результат деления.
10. Внутри функции divide определена вспомогательная рекурсивная функция help, которая выполняет деление с использованием алгоритма разделяй и властвуй.
11. Функция help рекурсивно вызывается до тех пор, пока dividend не станет меньше divisor.
12. На каждой итерации цикла в help увеличивается счетчик res, который дает количество раз, на которое divisor полностью помещается в dividend.
13. Рекурсивным вызовом help обрабатывается остаток от деления dividend на исходный divisor.
🤯52
Задача: 30. Substring with Concatenation of All Words #hard

Условие:
Вам дана строка s и массив строк-слов. Все строки слов имеют одинаковую длину.
Объединенная строка — это строка, которая в точности содержит все строки любой перестановки объединенных слов.
Например, если слова = ["ab", "cd", "ef"], то "abcdef", "abefcd", "cdabef", "cdefab", "efabcd" и "efcdab" являются объединенными строками. «acdbef» не является объединенной строкой, поскольку не является объединением какой-либо перестановки слов.Возвращает массив начальных индексов всех объединенных подстрок в s. Вы можете вернуть ответ в любом порядке.

Решение:
var findSubstring = function(s, words) {
let len = words[0].length; let windowSize = words.length * len;
if (s.length < windowSize) { return [];
} let m = new Map(), res = [];
// Fill Hash table with entry being (word, number of occurrences) for (let i = 0; i < words.length; i++) {
m.set(words[i], m.get(words[i]) + 1 1); }
let start = 0; while (start + windowSize - 1 < s.length) {
if (isConcat(s, new Map(m), len, start, start + windowSize-1)) { res.push(start);
} start++;
} return res;
// Let M be the length of s, N be the number of words // T.C: O(M*N)
// S.C: O(M*N), new map is used for every iteration};
const isConcat = (s, m, len, start, end) => {
let chars = m.size; for (let i = start; i <= end; i += len) {
let substr = s.substring(i, i+len); if (!m.has(substr) m.get(substr) === 0) return false;
m.set(substr, m.get(substr) - 1); if (m.get(substr) === 0) {
chars--; }
} return chars === 0;
// if we consider time complexity of substring() to be O(1), // T.C: O(N)
}

Пояснение:
1. В функции findSubstring сначала определяется длина len первого слова из массива words и общий размер окна windowSize для всех слов из массива. Если длина входной строки s меньше размера окна, то функция возвращает пустой массив.
2. Создается пустая хэш-таблица m для хранения количества вхождений каждого слова из массива words и массив res для хранения индексов начала подстроки в строке s.
3. Заполняется хэш-таблица m с записями вида (слово, количество вхождений) для каждого слова из массива words.
4. Далее производится поэлементный обход строки s с шагом 1, начиная с индекса start. Для каждой итерации проверяется, является ли подстрока с текущим окном возможной конкатенацией слов из массива words с помощью функции isConcat.
5. Если проверка в isConcat возвращает true, текущий индекс start добавляется в массив res как начало валидной подстроки.
6. После завершения обхода возвращается массив res, содержащий индексы начала валидных подстрок в строке s.
7. Вспомогательная функция isConcat проверяет, является ли данная подстрока валидной конкатенацией слов из хэш-таблицы m. Она обходит подстроку с шагом len, уменьшая количество оставшихся вхождений для каждого слова в хэш-таблице, пока не исчерпаются все слова. Функция возвращает true в случае успешного поиска, иначе false.
8. Общая сложность времени для функции findSubstring равна O(M*N), где M - длина входной строки s, N - количество слов в массиве words, а пространственная сложность составляет O(M*N) из-за использования новой хэш-таблицы на каждой итерации. Функция isConcat имеет линейную сложность от размера окна N.
👍75🤯4