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

Тесты t.me/+T0COHtFzCJkwMDUy
Вопросы собесов t.me/+kXKgJEjRUww3N2Ni
Вакансии t.me/+CgCAzIyGHHg0Nzky
Download Telegram
Задача: 1535. Find the Winner of an Array Game
Сложность: medium

Дан целочисленный массив arr из различных целых чисел и целое число k.

Игра будет проводиться между первыми двумя элементами массива (т.е. arr[0] и arr[1]). В каждом раунде игры мы сравниваем arr[0] с arr[1], большее число побеждает и остается на позиции 0, а меньшее число перемещается в конец массива. Игра заканчивается, когда одно число выигрывает k подряд раундов.

Верните число, которое выиграет игру.

Гарантируется, что у игры будет победитель.

Пример:
Input: arr = [2,1,3,5,4,6,7], k = 2
Output: 5
Explanation: Let's see the rounds of the game:
Round | arr | winner | win_count
1 | [2,1,3,5,4,6,7] | 2 | 1
2 | [2,3,5,4,6,7,1] | 3 | 1
3 | [3,5,4,6,7,1,2] | 5 | 1
4 | [5,4,6,7,1,2,3] | 5 | 2
So we can see that 4 rounds will be played and 5 is the winner because it wins 2 consecutive games.


👨‍💻 Алгоритм:

1⃣Инициализируйте maxElement как максимальный элемент в arr, queue как очередь с элементами массива, кроме первого, curr = arr[0] и winstreak = 0.

2⃣Пока очередь не пуста (или используйте бесконечный цикл), извлеките opponent из начала очереди. Если curr > opponent, добавьте opponent в конец очереди и увеличьте winstreak на 1. В противном случае добавьте curr в конец очереди, установите curr = opponent и winstreak = 1.

3⃣Если winstreak = k или curr = maxElement, верните curr. Код никогда не должен достигать этой точки, так как гарантированно есть победитель. Верните любое значение.

😎 Решение
class Solution {
getWinner(arr, k) {
let maxElement = arr[0];
let queue = arr.slice(1);

for (let element of arr.slice(1)) {
maxElement = Math.max(maxElement, element);
}

let curr = arr[0];
let winstreak = 0;

while (queue.length > 0) {
let opponent = queue.shift();

if (curr > opponent) {
queue.push(opponent);
winstreak++;
} else {
queue.push(curr);
curr = opponent;
winstreak = 1;
}

if (winstreak === k || curr === maxElement) {
return curr;
}
}

return -1;
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯1
Forwarded from easyoffer
На easyoffer 2.0 появится:
База тестовых заданий

🟠Тестовые задания для разных грейдов
🟠Фильтрация тестовых заданий по технологиям и компаниям

Когда я только начинал учиться на программиста, я постоянно выдумывал себе задачи для практики и тратил на это много времени. Но только в момент поиска работы я столкнулся с тестовыми заданиями, и понял насколько круто они прокачивают навыки. Нужно было еще на этапе обучения пробовать их делать. Все компании стараются составить тестовое задание "под себя", это дает большой выбор в тематике задач и технологий. На easyoffer 2.0 вы сможете отфильтровать тестовые задания по навыкам/грейдам и найти те, что подходят лично вам для практики.

В течение 1-2 дней я объявлю о краудфандинг кампании, чтобы ускорить разработку easyoffer 2.0. Все кто, поддержал проект на этом этапе смогу получить 1 год доступа к сайту по цене месячной подписки и смогут попасть на закрытое бета-тестирование. А первые 150 донатеров получать особо-выгодную цену и бонус.

🚀 Следите за стартом 👉 в этом телеграм канале, в нем информация о старте будет опубликована за 6 часов до официального начала.
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1162. As Far from Land as Possible
Сложность: medium

Дана сетка размером n x n, содержащая только значения 0 и 1, где 0 представляет воду, а 1 представляет землю. Найдите ячейку с водой, такое что её расстояние до ближайшей ячейки с землёй максимально, и верните это расстояние. Если в сетке нет ни земли, ни воды, верните -1.

Расстояние, используемое в этой задаче, - это манхэттенское расстояние: расстояние между двумя ячейками (x0, y0) и (x1, y1) равно |x0 - x1| + |y0 - y1|.

Пример:
Input: grid = [[1,0,1],[0,0,0],[1,0,1]]
Output: 2
Explanation: The cell (1, 1) is as far as possible from all the land with distance 2.


👨‍💻 Алгоритм:

1⃣Итерируйте по матрице и вставьте координаты ячеек с землёй в очередь. Инициализируйте переменную distance значением -1 для хранения текущего шага обхода в ширину (BFS). Также создайте копию матрицы visited для пометки ячеек с водой как посещённые, чтобы не вставлять их снова в очередь.

2⃣Выполните BFS: Обходите текущие элементы в очереди и для каждого элемента проверяйте координаты в четырёх направлениях, являются ли они ячейками с водой (0). Если да, вставьте их в очередь и измените их на ячейки с землёй (1) в матрице visited. После каждого пройденного уровня (внутренний цикл while завершён), увеличьте переменную distance.

3⃣Повторяйте, пока очередь не станет пустой. Верните значение distance. Если оно равно 0, это означает, что не было ячеек с водой и обход завершился после первого шага, поэтому верните -1. Если в матрице не было ячеек с землёй, цикл while вообще не начнётся, и переменная distance останется с начальным значением (-1).

😎 Решение:
var maxDistance = function(grid) {
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
const visited = grid.map(row => row.slice());
const q = [];

for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j] === 1) {
q.push([i, j]);
}
}
}

let distance = -1;
while (q.length > 0) {
const qSize = q.length;
for (let i = 0; i < qSize; i++) {
const [landX, landY] = q.shift();
for (const [dx, dy] of directions) {
const x = landX + dx;
const y = landY + dy;
if (x >= 0 && y >= 0 && x < grid.length && y < grid[0].length && visited[x][y] === 0) {
visited[x][y] = 1;
q.push([x, y]);
}
}
}
distance++;
}

return distance === 0 ? -1 : distance;
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Задача: 894. All Possible Full Binary Trees
Сложность: medium

Учитывая целое число n, верните список всех возможных полных бинарных деревьев с узлами. Каждый узел каждого дерева в ответе должен иметь Node.val == 0. Каждый элемент ответа является корневым узлом одного возможного дерева. Вы можете вернуть конечный список деревьев в любом порядке. Полное бинарное дерево - это бинарное дерево, в котором каждый узел имеет ровно 0 или 2 дочерних.

Пример:
Input: n = 7
Output: [[0,0,0,null,null,0,0,null,null,0,0],[0,0,0,null,null,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,null,null,null,null,0,0],[0,0,0,0,0,null,null,0,0]]


👨‍💻 Алгоритм:

1⃣Если n четное, вернуть пустой список, так как полное бинарное дерево не может иметь четное количество узлов.

2⃣Если n == 1, вернуть дерево с одним узлом. Для всех возможных значений i от 1 до n-1 с шагом 2: Создать левое поддерево с i узлами. Создать правое поддерево с n-1-i узлами. Объединить все комбинации левого и правого поддеревьев с новым корнем, добавив их в список результатов.

3⃣Вернуть список результатов.

😎 Решение:
function TreeNode(val, left, right) {
this.val = (val===undefined ? 0 : val)
this.left = (left===undefined ? null : left)
this.right = (right===undefined ? null : right)
}

var allPossibleFBT = function(n) {
if (n % 2 === 0) {
return [];
}
if (n === 1) {
return [new TreeNode(0)];
}

const result = [];
for (let i = 1; i < n; i += 2) {
const leftTrees = allPossibleFBT(i);
const rightTrees = allPossibleFBT(n - 1 - i);
for (const left of leftTrees) {
for (const right of rightTrees) {
result.push(new TreeNode(0, left, right));
}
}
}
return result;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1672. Richest Customer Wealth
Сложность: easy

Вам дан целочисленный массив размером m x n под названием accounts, где accounts[i][j] — это сумма денег, которую i-й клиент имеет в j-м банке. Верните богатство самого богатого клиента.

Богатство клиента — это сумма денег, которую он имеет во всех своих банковских счетах. Самый богатый клиент — это клиент, который имеет максимальное богатство.

Пример:
Input: accounts = [[1,2,3],[3,2,1]]
Output: 6
Explanation:
1st customer has wealth = 1 + 2 + 3 = 6
2nd customer has wealth = 3 + 2 + 1 = 6
Both customers are considered the richest with a wealth of 6 each, so return 6.


👨‍💻 Алгоритм:

1⃣Пройдите по всем клиентам в массиве accounts.

2⃣Для каждого клиента вычислите сумму денег на всех его банковских счетах и сравните её с максимальным богатством, найденным до этого момента.

3⃣Если текущее богатство больше максимального, обновите максимальное значение. Верните максимальное богатство.

😎 Решение:
var maximumWealth = function(accounts) {
let maxWealthSoFar = 0;

for (let account of accounts) {
let currCustomerWealth = account.reduce((acc, money) => acc + money, 0);
maxWealthSoFar = Math.max(maxWealthSoFar, currCustomerWealth);
}

return maxWealthSoFar;
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 936. Stamping The Sequence
Сложность: hard

Вам даны две строки stamp и target. Изначально имеется строка s длины target.length со всеми s[i] == '?'. За один ход вы можете поместить штамп над s и заменить каждую букву в s на соответствующую букву из штампа. Например, если штамп = "abc" и target = "abcba", то s изначально будет "?????". За один ход вы можете: поместить штамп в индекс 0 s, чтобы получить "abc??", поместить штамп в индекс 1 s, чтобы получить "?abc?", или поместить штамп в индекс 2 s, чтобы получить "??abc". Обратите внимание, что штамп должен полностью находиться в границах s, чтобы штамповать (то есть вы не можете поместить штамп в индекс 3 s). Мы хотим преобразовать s в цель, используя не более 10 * target.length ходов. Верните массив индекса самой левой буквы, которая штампуется на каждом ходу. Если мы не можем получить цель из s за 10 * target.length оборотов, верните пустой массив

Пример:
Input: stamp = "abc", target = "ababc"
Output: [0,2]


👨‍💻 Алгоритм:

1⃣Инициализировать переменные:
s как массив символов '?', длиной target.length.
res как список для хранения результатов.
done как массив булевых значений для отслеживания того, какие позиции уже штампованы.
target как массив символов для удобства.

2⃣Использовать функцию canStamp для проверки, можно ли штамповать stamp в target начиная с индекса i.
Использовать функцию doStamp для штампования stamp в target начиная с индекса i.
Повторять шаги, пока штампы возможны или достигнут максимум ходов (10 * target.length):
Перебрать все возможные начальные позиции для штампа.
Проверить, можно ли штамповать в текущей позиции.
Если можно, штамповать и добавить индекс в res.

3⃣Если все символы в s соответствуют символам в target, вернуть массив res в обратном порядке. Иначе, вернуть пустой массив.

😎 Решение:
var movesToStamp = function(stamp, target) {
const s = stamp.split(''), t = target.split('');
const m = s.length, n = t.length;
const res = [];
const done = new Array(n).fill(false);

function canStamp(i) {
for (let j = 0; j < m; j++) {
if (t[i + j] !== '?' && t[i + j] !== s[j]) {
return false;
}
}
return true;
}

function doStamp(i) {
for (let j = 0; j < m; j++) {
t[i + j] = '?';
}
res.push(i);
done[i] = true;
}

let changed = true;
while (changed) {
changed = false;
for (let i = 0; i <= n - m; i++) {
if (!done[i] && canStamp(i)) {
doStamp(i);
changed = true;
}
}
}

return t.every(c => c === '?') ? res.reverse() : [];
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from easyoffer
🎉 Краудфандинг easyoffer 2.0 стартовал!

Друзья, с этого момента вы можете поддержать проект и получить существенный бонус:

🚀 PRO-тариф на 1 год, по цене месячной подписки на релизе.
Доступ к закрытому бета-тесту easyoffer 2.0 (середина–конец мая)

Поддержать проект можно здесь:
https://planeta.ru/campaigns/easyoffer

📌 Если не получается оплатить через карту РФ — напишите мне @kivaiko, и мы найдём удобный способ
Forwarded from easyoffer
Я поставил целью сбора скромные 300 тыс. рублей, но ребята, вы накидали больше млн. всего за 1 день. Это просто невероятно!

Благодаря вашей поддержке, я смогу привлечь еще больше людей для разработки сайта и обработки собеседований. Ваш вклад сделает проект качественнее и ускорит его выход! Огромное вам спасибо!

Краудфандинг будет продолжаться еще 31 день и все кто поддержать проект сейчас, до его выхода, смогут получить:

🚀 PRO-тариф на 1 год, по цене месячной подписки на релизе.
Доступ к закрытому бета-тесту easyoffer 2.0 (середина–конец мая)

Поддержать проект можно здесь:
https://planeta.ru/campaigns/easyoffer

Огромное спасибо за вашу поддержку! 🤝
🔥1
Задача: 939. Minimum Area Rectangle
Сложность: medium

Дан массив точек в плоскости X-Y points, где points[i] = [xi, yi]. Верните минимальную площадь прямоугольника, образованного из этих точек, со сторонами, параллельными осям X и Y. Если такого прямоугольника не существует, верните 0.

Пример:
Input: points = [[1,1],[1,3],[3,1],[3,3],[2,2]]
Output: 4


👨‍💻 Алгоритм:

1⃣Создать множество для хранения всех точек.
Инициализировать переменную для хранения минимальной площади прямоугольника с бесконечным значением.

2⃣Пройти через все пары точек:
Если две точки могут образовать диагональ прямоугольника, то проверить, существуют ли оставшиеся две точки для замкнутого прямоугольника.
Если да, вычислить площадь прямоугольника и обновить минимальную площадь.

3⃣Если минимальная площадь остается бесконечной, вернуть 0. Иначе вернуть минимальную площадь.

😎 Решение:
var minAreaRect = function(points) {
const pointSet = new Set(points.map(p => `${p[0]},${p[1]}`));
let minArea = Infinity;

for (let i = 0; i < points.length; i++) {
for (let j = i + 1; j < points.length; j++) {
const [x1, y1] = points[i];
const [x2, y2] = points[j];
if (x1 !== x2 && y1 !== y2 && pointSet.has(`${x1},${y2}`) && pointSet.has(`${x2},${y1}`)) {
minArea = Math.min(minArea, Math.abs(x2 - x1) * Math.abs(y2 - y1));
}
}
}

return minArea === Infinity ? 0 : minArea;
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 346. Moving Average from Data Stream
Сложность: easy

Дан поток целых чисел и размер окна, вычислите скользящее среднее всех целых чисел в скользящем окне.

Реализуйте класс MovingAverage:

MovingAverage(int size) Инициализирует объект с размером окна size.
double next(int val) Возвращает скользящее среднее последних size значений потока.

Пример:
Input
["MovingAverage", "next", "next", "next", "next"]
[[3], [1], [10], [3], [5]]
Output
[null, 1.0, 5.5, 4.66667, 6.0]


👨‍💻 Алгоритм:

1⃣Инициализация:
Инициализируйте объект с фиксированным размером окна size.
Используйте очередь или список для хранения значений в текущем окне.
Храните текущую сумму значений в окне.

2⃣Добавление нового значения:
Добавьте новое значение в очередь.
Обновите текущую сумму, добавив новое значение.
Если размер очереди превышает size, удалите самое старое значение и обновите сумму, вычтя это значение.

3⃣Вычисление скользящего среднего:
Возвращайте текущее скользящее среднее как сумму значений в окне, деленную на количество значений в окне.

😎 Решение:
var MovingAverage = function(size) {
this.size = size;
this.queue = [];
this.sum = 0;
};

MovingAverage.prototype.next = function(val) {
this.queue.push(val);
this.sum += val;
if (this.queue.length > this.size) {
this.sum -= this.queue.shift();
}
return this.sum / this.queue.length;
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1059. All Paths from Source Lead to Destination
Сложность: medium

Учитывая ребра направленного графа, где edges[i] = [ai, bi] указывает на наличие ребра между вершинами ai и bi, и две вершины source и destination этого графа, определите, все ли пути, начинающиеся из source, заканчиваются в destination, то есть: существует ли хотя бы один путь из source в destination Если существует путь из source в node без исходящих ребер, то этот node равен destination. Количество возможных путей из source в destination - конечное число. Верните true тогда и только тогда, когда все пути из source ведут в destination.

Пример:
Input: n = 3, edges = [[0,1],[0,2]], source = 0, destination = 2
Output: false


👨‍💻 Алгоритм:

1⃣Построение графа и проверка путей:
Построить граф на основе входных данных.
Использовать поиск в глубину (DFS) для проверки наличия всех путей от вершины source до вершины destination.

2⃣Проверка конечности путей:
Проверить, что из всех вершин, достижимых от source, либо исходят ребра, либо они являются вершиной destination.
Убедиться, что из любой вершины, не являющейся destination, исходят хотя бы одно ребро.

3⃣Рекурсивная проверка конечности путей:
Рекурсивно проверять, что все пути из source заканчиваются в destination, избегая циклов и проверяя конечность всех путей.

😎 Решение:
var leadsToDestination = function(n, edges, source, destination) {
const graph = {};
for (const [a, b] of edges) {
if (!graph[a]) graph[a] = [];
graph[a].push(b);
}

const visited = new Array(n).fill(0);

function dfs(node) {
if (visited[node] !== 0) return visited[node] === 2;
if (!graph[node]) return node === destination;
visited[node] = 1;
for (const neighbor of graph[node]) {
if (!dfs(neighbor)) return false;
}
visited[node] = 2;
return true;
}

return dfs(source);
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 983. Minimum Cost For Tickets
Сложность: medium

Вы запланировали несколько поездок на поезде за год вперёд. Дни года, в которые вы будете путешествовать, заданы в виде целочисленного массива days. Каждый день — это целое число от 1 до 365.

Билеты на поезд продаются тремя различными способами:

однодневный билет продаётся за costs[0] долларов,
семидневный билет продаётся за costs[1] долларов, и
тридцатидневный билет продаётся за costs[2] долларов.
Билеты позволяют путешествовать указанное количество дней подряд.

Например, если мы покупаем семидневный билет на 2-й день, то мы можем путешествовать в течение 7 дней: 2, 3, 4, 5, 6, 7 и 8.
Верните минимальное количество долларов, которое вам нужно, чтобы путешествовать каждый день, указанный в списке days.

Пример:
Input: days = [1,4,6,7,8,20], costs = [2,7,15]
Output: 11
Explanation: For example, here is one way to buy passes that lets you travel your travel plan:
On day 1, you bought a 1-day pass for costs[0] = $2, which covered day 1.
On day 3, you bought a 7-day pass for costs[1] = $7, which covered days 3, 4, ..., 9.
On day 20, you bought a 1-day pass for costs[0] = $2, which covered day 20.
In total, you spent $11 and covered all the days of your travel.


👨‍💻 Алгоритм:

1⃣Создайте массив dp размером на один больше последнего дня, в который нужно путешествовать. Инициализируйте все значения -1, что означает, что ответ для этого дня ещё не был вычислен. Также создайте хэш-набор isTravelNeeded из days.

2⃣Создайте функцию solve, которая принимает аргумент currDay. Если currDay больше последнего дня, когда нужно путешествовать, просто верните 0, так как все дни уже покрыты. Если currDay отсутствует в isTravelNeeded, перейдите к currDay + 1. Если ответ для currDay в массиве dp не равен -1, это означает, что ответ уже был вычислен, поэтому просто верните его.

3⃣Найдите стоимость трёх билетов, которые можно использовать в этот день, добавьте соответствующую стоимость и обновите dp[currDay] соответственно в рекурсивном вызове. Вызовите solve, передав currDay = 1, и верните ответ.

😎 Решение:
class Solution {
constructor() {
this.isTravelNeeded = new Set();
}

solve(dp, days, costs, currDay) {
if (currDay > days[days.length - 1]) {
return 0;
}

if (!this.isTravelNeeded.has(currDay)) {
return this.solve(dp, days, costs, currDay + 1);
}

if (dp[currDay] != -1) {
return dp[currDay];
}

let oneDay = costs[0] + this.solve(dp, days, costs, currDay + 1);
let sevenDay = costs[1] + this.solve(dp, days, costs, currDay + 7);
let thirtyDay = costs[2] + this.solve(dp, days, costs, currDay + 30);

dp[currDay] = Math.min(oneDay, Math.min(sevenDay, thirtyDay));
return dp[currDay];
}

mincostTickets(days, costs) {
let lastDay = days[days.length - 1];
let dp = new Array(lastDay + 1).fill(-1);

for (let day of days) {
this.isTravelNeeded.add(day);
}

return this.solve(dp, days, costs, 1);
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 893. Groups of Special-Equivalent Strings
Сложность: medium

Вам дан массив строк одинаковой длины words. За один ход вы можете поменять местами любые два четных или любые два нечетных символа строки words[i]. Две строки words[i] и words[j] являются специально-эквивалентными, если после любого количества ходов words[i] == words[j].

Например, words[i] = "zzxy" и words[j] = "xyzz" являются специально-эквивалентными, потому что мы можем делать ходы "zzxy" -> "xzzy" -> "xyzz". Группа специально-эквивалентных строк из слов - это непустое подмножество слов, такое, что: каждая пара строк в группе специально-эквивалентна, и группа имеет максимально возможный размер (т.е, не существует строки words[i], не входящей в группу, такой, что words[i] является специально-эквивалентной каждой строке в группе). Верните количество групп специально-эквивалентных строк из слов.

Пример:
Input: words = ["abcd","cdab","cbad","xyzz","zzxy","zzyx"]
Output: 3


👨‍💻 Алгоритм:

1⃣Для каждой строки в массиве words создать два новых списка: один из символов на четных позициях, другой из символов на нечетных позициях. Отсортировать оба списка и объединить их в одну строку, которая будет представлять каноническую форму строки.

2⃣Использовать множество, чтобы хранить все уникальные канонические формы строк.

3⃣Размер множества будет равен количеству групп специально-эквивалентных строк.

😎 Решение:
var numSpecialEquivGroups = function(words) {
const uniqueForms = new Set();

for (let word of words) {
const evenChars = word.split('').filter((_, i) => i % 2 === 0).sort().join('');
const oddChars = word.split('').filter((_, i) => i % 2 !== 0).sort().join('');
const canonicalForm = evenChars + oddChars;
uniqueForms.add(canonicalForm);
}

return uniqueForms.size;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 948. Bag of Tokens
Сложность: medium

Вы начинаете с начальной силой, равной power, начальным счетом 0 и мешком жетонов, представленным в виде целочисленного массива tokens, где каждый tokens[i] обозначает значение tokeni. Ваша цель - максимизировать общее количество очков, стратегически разыгрывая эти жетоны. За один ход вы можете разыграть неразыгранный жетон одним из двух способов (но не обоими для одного и того же жетона): лицом вверх: Если ваша текущая сила не меньше жетонов[i], вы можете сыграть токени, потеряв силу жетонов[i] и получив 1 очко. Лицом вниз: Если ваш текущий счет не меньше 1, вы можете сыграть токени, получив силу токенов[i] и потеряв 1 счет. Верните максимально возможный счет, который вы можете получить, сыграв любое количество токенов.

Пример:
Input: tokens = [100], power = 50

Output: 0


👨‍💻 Алгоритм:

1⃣Отсортировать массив tokens.
Использовать два указателя: left и right, чтобы отслеживать начало и конец массива токенов.
Инициализировать переменные для текущей силы power, текущего счета score и максимального счета maxScore

2⃣Повторять следующие шаги, пока left не превысит right:
Если текущая сила power достаточна для разыгрывания токена tokens[left] лицом вверх, разыграть его и увеличить счет.
Если текущая сила недостаточна, но счет больше 0, разыграть токен tokens[right] лицом вниз и уменьшить счет.
Обновить максимальный счет, если текущий счет больше максимального.

3⃣Вернуть максимальный счет.

😎 Решение:
var bagOfTokensScore = function(tokens, power) {
tokens.sort((a, b) => a - b);
let left = 0, right = tokens.length - 1;
let score = 0, maxScore = 0;

while (left <= right) {
if (power >= tokens[left]) {
power -= tokens[left];
score++;
left++;
maxScore = Math.max(maxScore, score);
} else if (score > 0) {
power += tokens[right];
score--;
right--;
} else {
break;
}
}

return maxScore;
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 349. Intersection of Two Arrays
Сложность: easy

Даны два целочисленных массива nums1 и nums2. Верните массив их пересечения. Каждый элемент в результате должен быть уникальным, и вы можете вернуть результат в любом порядке.

Пример:
Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2]


👨‍💻 Алгоритм:

1⃣Создание множеств:
Преобразуйте оба массива nums1 и nums2 в множества для получения уникальных элементов.

2⃣Нахождение пересечения:
Найдите пересечение двух множеств.

3⃣Возврат результата:
Преобразуйте пересечение обратно в массив и верните его.

😎 Решение:
var intersection = function(nums1, nums2) {
let set1 = new Set(nums1);
let set2 = new Set(nums2);
let result = [];

for (let num of set1) {
if (set2.has(num)) {
result.push(num);
}
}

return result;
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Задача: 1125. Smallest Sufficient Team
Сложность: hard

В проекте у вас есть список необходимых навыков req_skills и список людей. i-й человек people[i] содержит список навыков, которыми обладает этот человек.

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

Например, команда = [0, 1, 3] представляет людей с навыками people[0], people[1] и people[3].
Верните любую достаточную команду наименьшего возможного размера, представленную индексами каждого человека. Вы можете вернуть ответ в любом порядке.

Гарантируется, что ответ существует.

Пример:
Input: req_skills = ["algorithms","math","java","reactjs","csharp","aws"],
people = [["algorithms","math","java"],["algorithms","math","reactjs"],
["java","csharp","aws"],["reactjs","csharp"],["csharp","math"],["aws","java"]]
Output: [1,2]


👨‍💻 Алгоритм:

1⃣Инициализация и создание масок навыков:
Определите количество людей n и количество необходимых навыков m.
Создайте хэш-таблицу skillId, чтобы сопоставить каждому навыку уникальный индекс.
Создайте массив skillsMaskOfPerson, который будет содержать битовые маски навыков для каждого человека.

2⃣Динамическое программирование (DP):
Создайте массив dp размера 2^m и заполните его значениями (1 << n) - 1.
Установите dp[0] в 0 (базовый случай).
Для каждого skillsMask от 1 до 2^m - 1:
- для каждого человека i:
- вычислите smallerSkillsMask как skillsMask & ~skillsMaskOfPerson[i].
- если smallerSkillsMask отличается от skillsMask, обновите dp[skillsMask], если новая команда лучше (имеет меньше установленных битов).

3⃣Формирование ответа:
Извлеките ответ из dp и верните массив индексов людей, составляющих минимальную достаточную команду.

😎 Решение:
var smallestSufficientTeam = function(req_skills, people) {
const n = people.length, m = req_skills.length;
const skillId = new Map(req_skills.map((skill, i) => [skill, i]));
const skillsMaskOfPerson = Array(n).fill(0);
for (let i = 0; i < n; i++) {
for (const skill of people[i]) {
if (skillId.has(skill)) {
skillsMaskOfPerson[i] |= 1 << skillId.get(skill);
}
}
}
const dp = Array(1 << m).fill((1 << n) - 1);
dp[0] = 0;
for (let skillsMask = 1; skillsMask < (1 << m); skillsMask++) {
for (let i = 0; i < n; i++) {
const smallerSkillsMask = skillsMask & ~skillsMaskOfPerson[i];
if (smallerSkillsMask !== skillsMask) {
const peopleMask = dp[smallerSkillsMask] | (1 << i);
if (bitCount(peopleMask) < bitCount(dp[skillsMask])) {
dp[skillsMask] = peopleMask;
}
}
}
}
const answerMask = dp[(1 << m) - 1];
const result = [];
for (let i = 0; i < n; i++) {
if ((answerMask >> i) & 1) {
result.push(i);
}
}
return result;
};

function bitCount(n) {
let count = 0;
while (n) {
count += n & 1;
n >>= 1;
}
return count;
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 786. K-th Smallest Prime Fraction
Сложность: medium

Вам дан отсортированный массив целых чисел arr, содержащий 1 и простые числа, где все элементы массива arr уникальны. Также дано целое число k.

Для каждого i и j, где 0 <= i < j < arr.length, мы рассматриваем дробь arr[i] / arr[j].

Верните k-ую наименьшую дробь из рассмотренных. Верните ответ в виде массива из двух целых чисел размера 2, где answer[0] == arr[i] и answer[1] == arr[j].

Пример:
Input: arr = [1,2,3,5], k = 3
Output: [2,5]
Explanation: The fractions to be considered in sorted order are:
1/5, 1/3, 2/5, 1/2, 3/5, and 2/3.
The third fraction is 2/5.


👨‍💻 Алгоритм:

1⃣Инициализируйте пустую приоритетную очередь pq для хранения пар дробей и соответствующих им индексов. Итеративно пройдите по входному массиву arr, используя переменную цикла i. Для каждого элемента arr[i] вычислите дробь, образованную делением его на наибольший элемент в массиве (arr[arr.size() - 1]). Поместите пару, состоящую из отрицательного значения дроби (-1.0 * arr[i] / arr[arr.size() - 1]) и соответствующих индексов (i для числителя и arr.size() - 1 для знаменателя), в приоритетную очередь pq. Приоритетная очередь pq теперь содержит все дроби, образованные делением каждого элемента на наибольший элемент в массиве, отсортированные в порядке возрастания значений дробей.

2⃣Повторите следующие шаги k - 1 раз: удалите верхний элемент (наименьшую дробь) из приоритетной очереди pq и сохраните его индексы в переменной cur. Уменьшите индекс знаменателя (cur[1]--). Вычислите новую дробь, образованную делением числителя в cur[0] на уменьшенный знаменатель (arr[cur[1]]). Поместите новое значение дроби (-1.0 * arr[cur[0]] / arr[cur[1]]) и соответствующие индексы (cur[0] для числителя и cur[1] для знаменателя) в приоритетную очередь pq. После k - 1 итераций верхний элемент приоритетной очереди pq будет k-й наименьшей дробью.

3⃣Извлеките индексы числителя и знаменателя из верхнего элемента приоритетной очереди и сохраните их в result. Верните массив, содержащий значения числителя (arr[result[0]]) и знаменателя (arr[result[1]]), соответствующие k-й наименьшей дроби.

😎 Решение:
class Solution {
kthSmallestPrimeFraction(arr, k) {
const pq = new MinPriorityQueue({ compare: (a, b) => a.fraction - b.fraction });

for (let i = 0; i < arr.length - 1; i++) {
pq.enqueue({
fraction: arr[i] / arr[arr.length - 1],
indices: [i, arr.length - 1]
});
}

for (let i = 0; i < k - 1; i++) {
const { indices } = pq.dequeue();
const [numeratorIndex, denominatorIndex] = indices;
if (denominatorIndex - 1 > numeratorIndex) {
pq.enqueue({
fraction: arr[numeratorIndex] / arr[denominatorIndex - 1],
indices: [numeratorIndex, denominatorIndex - 1]
});
}
}

const result = pq.dequeue().indices;
return [arr[result[0]], arr[result[1]]];
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 910. Smallest Range II
Сложность: medium

Вам дан целочисленный массив nums и целое число k. Для каждого индекса i, где 0 <= i < nums.length, измените nums[i] на nums[i] + k или nums[i] - k. Оценка nums - это разница между максимальным и минимальным элементами в nums. Верните минимальную оценку nums после изменения значений в каждом индексе.

Пример:
Input: nums = [1], k = 0
Output: 0


👨‍💻 Алгоритм:

1⃣Отсортировать массив nums.

2⃣Рассчитать начальную разницу между максимальным и минимальным элементами.

3⃣Пройтись по всем элементам массива, пытаясь минимизировать разницу, изменяя текущий элемент на +k и -k и вычисляя новые максимальные и минимальные значения массива.

😎 Решение:
var smallestRangeII = function(nums, k) {
nums.sort((a, b) => a - b);
let n = nums.length;
let result = nums[n - 1] - nums[0];
let minVal = nums[0];
let maxVal = nums[n - 1];

for (let i = 0; i < n - 1; i++) {
let high = Math.max(nums[i] + k, maxVal - k);
let low = Math.min(nums[i + 1] - k, minVal + k);
result = Math.min(result, high - low);
}

return result;
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
1🤯1
Задача: 815. Bus Routes
Сложность: hard

Дан массив routes, представляющий автобусные маршруты, где routes[i] - это автобусный маршрут, который i-й автобус повторяет бесконечно.

Например, если routes[0] = [1, 5, 7], это означает, что 0-й автобус путешествует в последовательности 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... бесконечно.
Вы начинаете на автобусной остановке source (вы изначально не находитесь в автобусе) и хотите добраться до автобусной остановки target. Перемещаться между автобусными остановками можно только на автобусах.

Верните наименьшее количество автобусов, которые вам нужно взять, чтобы доехать от source до target. Верните -1, если это невозможно.

Пример:
Input: routes = [[1,2,7],[3,6,7]], source = 1, target = 6
Output: 2
Explanation: The best strategy is take the first bus to the bus stop 7, then take the second bus to the bus stop 6.


👨‍💻 Алгоритм:

1⃣Верните 0, если source и target совпадают. Инициализируйте пустую карту adjList, чтобы хранить ребра, где ключ - это автобусная остановка, а значение - список целых чисел, обозначающих индексы маршрутов, которые имеют эту остановку. Инициализируйте пустую очередь q и неупорядоченное множество vis, чтобы отслеживать посещенные маршруты. Вставьте начальные маршруты в очередь q и отметьте их посещенными в vis.

2⃣Итерация по очереди, пока она не пуста: извлеките маршрут из очереди, итерируйтесь по остановкам в маршруте. Если остановка равна target, верните busCount. В противном случае, итерируйтесь по маршрутам для этой остановки в карте adjList, добавьте непосещенные маршруты в очередь и отметьте их посещенными.

3⃣Верните -1 после завершения обхода в ширину (BFS).

😎 Решение:
var numBusesToDestination = function(routes, source, target) {
if (source === target) return 0;

const adjList = new Map();
routes.forEach((stops, route) => {
stops.forEach(stop => {
if (!adjList.has(stop)) {
adjList.set(stop, []);
}
adjList.get(stop).push(route);
});
});

const q = [];
const vis = new Set();
(adjList.get(source) || []).forEach(route => {
q.push(route);
vis.add(route);
});

let busCount = 1;
while (q.length > 0) {
const size = q.length;
for (let i = 0; i < size; i++) {
const route = q.shift();
for (const stop of routes[route]) {
if (stop === target) return busCount;
for (const nextRoute of (adjList.get(stop) || [])) {
if (!vis.has(nextRoute)) {
vis.add(nextRoute);
q.push(nextRoute);
}
}
}
}
busCount++;
}
return -1;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 787. Cheapest Flights Within K Stops
Сложность: medium

Есть n городов, соединенных некоторым количеством рейсов. Вам дан массив flights, где flights[i] = [fromi, toi, pricei] указывает на то, что существует рейс из города fromi в город toi с ценой pricei.

Также даны три целых числа src, dst и k. Верните самую дешевую цену от src до dst с не более чем k остановками. Если такого маршрута нет, верните -1.

Пример:
Input: n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1
Output: 700
Explanation:
The graph is shown above.
The optimal path with at most 1 stop from city 0 to 3 is marked in red and has cost 100 + 600 = 700.
Note that the path through cities [0,1,2,3] is cheaper but is invalid because it uses 2 stops.


👨‍💻 Алгоритм:

1⃣Создайте список смежности, где adj[X] содержит всех соседей узла X и соответствующую цену, которую нужно заплатить, чтобы перейти к соседу. Инициализируйте массив dist, хранящий минимальную цену для достижения узла из узла src. Инициализируйте его большими значениями. Инициализируйте очередь, хранящую пары {node, distance}. Изначально очередь должна содержать только {src, 0}. Создайте переменную stops и установите ее значение равным 0.

2⃣Выполняйте поиск в ширину (BFS), пока очередь не станет пустой или пока stops > k. Итерируйте по всем узлам на определенном уровне. Это будет сделано путем запуска вложенного цикла и посещения всех узлов, которые в данный момент находятся в очереди. В каждой паре {node, distance} итерируйте по всем соседям узла. Для каждого соседа проверьте, меньше ли dist[neighbor] чем distance + цена ребра. Если это так, обновите dist[neighbor] и добавьте {neighbor, dist[neighbor]} в очередь.

3⃣После итерации по всем узлам на текущем уровне увеличьте stops на один. Мы посетили все узлы на определенном уровне и готовы посетить следующий уровень узлов. Когда мы достигнем условия, при котором либо очередь станет пустой, либо stops == k, у нас будет наш ответ в dist[dst]. Если dist[dst] не изменилось с начального большого значения, значит, мы никогда не достигли его, и следует вернуть -1.

😎 Решение:
class Solution {
findCheapestPrice(n, flights, src, dst, k) {
const adj = Array.from({ length: n }, () => []);
for (const [from, to, price] of flights) {
adj[from].push([to, price]);
}
const dist = Array(n).fill(Infinity);
const q = [[src, 0]];
let stops = 0;

while (stops <= k && q.length) {
const size = q.length;
for (let i = 0; i < size; i++) {
const [node, distance] = q.shift();
for (const [neighbor, price] of adj[node]) {
if (price + distance >= dist[neighbor]) continue;
dist[neighbor] = price + distance;
q.push([neighbor, dist[neighbor]]);
}
}
stops++;
}
return dist[dst] === Infinity ? -1 : dist[dst];
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Задача: 1326. Minimum Number of Taps to Open to Water a Garden
Сложность: hard

Есть одномерный сад на оси x. Сад начинается в точке 0 и заканчивается в точке n. (т.е. длина сада равна n).

В саду есть n + 1 кранов, расположенных в точках [0, 1, ..., n].

Даны целое число n и целочисленный массив ranges длиной n + 1, где ranges[i] (индексация начинается с 0) означает, что i-й кран может поливать область [i - ranges[i], i + ranges[i]], если он открыт.

Верните минимальное количество кранов, которые должны быть открыты для полива всего сада. Если сад невозможно полить, верните -1.

Пример:
Input: n = 5, ranges = [3,4,1,1,0,0]
Output: 1
Explanation: The tap at point 0 can cover the interval [-3,3]
The tap at point 1 can cover the interval [-3,5]
The tap at point 2 can cover the interval [1,3]
The tap at point 3 can cover the interval [2,4]
The tap at point 4 can cover the interval [4,4]
The tap at point 5 can cover the interval [5,5]
Opening Only the second tap will water the whole garden [0,5]


👨‍💻 Алгоритм:

1⃣Объявите массив dp размера n+1. Инициализируйте его значениями бесконечности (в коде используем большое число 10^9 для представления бесконечности). Установите dp[0] в 0 (базовый случай DP).

2⃣Итерируйтесь от i до n (через каждый кран слева направо). Рассчитайте самую левую позицию, достижимую текущим краном, как tap_start=max(0,i−ranges[i]). И самую правую позицию tap_end=min(n,i+ranges[i]).

3⃣Итерируйтесь через позиции j от tap_start до tap_end (в пределах досягаемости крана). Обновите dp[tap_end] значением dp[j]+1, если оно меньше. Если dp[n] остается бесконечным, значит, полить весь сад невозможно, и мы возвращаем −1. Верните dp[n].

😎 Решение:
var minTaps = function(n, ranges) {
const INF = Number.MAX_SAFE_INTEGER;
const dp = new Array(n + 1).fill(INF);
dp[0] = 0;

for (let i = 0; i <= n; i++) {
const tapStart = Math.max(0, i - ranges[i]);
const tapEnd = Math.min(n, i + ranges[i]);

for (let j = tapStart; j <= tapEnd; j++) {
dp[tapEnd] = Math.min(dp[tapEnd], dp[j] + 1);
}
}

return dp[n] === INF ? -1 : dp[n];
};


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM