Algorithmics: хакаем алгоритмические собесы
1.59K subscribers
17 photos
76 links
Канал для людей, жаждущих совершенствования в мире программирования.

Здесь вы найдете глубокие знания об алгоритмах, структурах данных и подготовке к собеседованиям в IT.

Авторы: @avivasyuta и @tifongod

Наш блог: https://algorithmics-blog.github.io/
Download Telegram
Структура для хранения уникальных значений

Давайте сегодня рассмотрим еще одну задачу на имплементацию структуры данных. Только в этот раз возьмем задачу не с Leetcode, а с реального интервью в Google.

Сложность: 🟠 Средняя


ℹ️ Описание

Реализуйте структуру данных для управления числами, которая имплементируют следующие методы:

▶️ Insert — добавляет новый элемент в структуру без создания дубликатов.

▶️ Remove — удаляет выбранный элемент из массива.

▶️ GetRandom — возвращает случайного элемент из ранее добавленных с равной вероятностью.

⚠️ Ограничения

Все методы должны работать с константной сложностью по времени — O(1).

1️⃣ Пример

Добавление значений в структуру.


const store = new Store()

store.insert(1)
store.insert(2)
store.insert(3)
store.insert(2)

// values — 1, 2, 3


2️⃣ Пример

Удаление значений из структуры.


const store = new Store()

store.insert(1)
store.insert(2)
store.insert(3)
store.remove(2)

// values — 1, 3


3️⃣ Пример

Получение случайного значения из структуры.


const store = new Store()

store.insert(1)
store.insert(2)
store.insert(3)

const value = store.getRandom()

// value — 1 (2 или 3 с одинаковой вероятностью)


Решение

Посмотреть решение

#arrays #maps #medium
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥21
Дневная температура

Всем привет!
Продолжаем закреплять знание структуры данных «стек» и умение определять этот паттерн. Мы уже разбирали самую мейнстримную задачу на валидацию скобочной последовательности. На прошлой неделе мы моделировали столкновение астероидов. Сегодня будем считать количество дней до ближайшего более теплого дня 🙂

Сложность: 🟠 Средняя

ℹ️ Описание

Напишите функцию, которая будет рассчитывать количество дней между текущим и днем с большей температурой.

Входные данные: Массив целых неотрицательных чисел. Каждое число в массиве, это температура в соответствующий день.

Выходные данные: Массив целых неотрицательных чисел. Каждое число в массиве, это количество дней от i'го дня до дня с большей температурой.

⚠️ Ограничения

- Количество дней (длина входного массива) находится в диапазоне от 1 до 100000
- Температура для каждого дня находится в диапазоне от 30 до 100

1️⃣Пример

Входящие данные

[73, 74, 75, 71, 69, 72, 76, 73]


Ответ

[1, 1, 4, 2, 1, 1, 0, 0]


2️⃣ Пример

Входящие данные

[30, 40, 50, 60]


Ответ

[1, 1, 1, 0]


3️⃣ Пример

Входящие данные

[0, 60, 90]


Ответ

[1, 1, 0]


Решение

Посмотреть подробное объяснение решения

#stack #medium
3🔥2😁1
Можно ли посадить цветы?

Всем привет!
Долго думал, какую задачку запостить перед новым годом 🙂 Сперва хотел найти что-нибудь интересное и хитрое, но в итоге решил, что думать перед новогодними каникулами не хочется. Поэтому можно просто расслабиться и решить что-нибудь простое и тривиальное.

P.S. Всех с наступающим и счастливого Нового Года!
Мы тоже уйдем на небольшие каникулы и вернемся к каналу после 9го января. Оставайтесь с нами, в новом году мы продолжим решать алгоритмические задачки, а также попробуем запустить еще один или несколько новых форматов 🙂

Сложность: 🟢 Легкая

ℹ️ Описание

Вам дан целочисленный массив flowerbed и число n.

Массив описывает цветочную клумбу, каждый элемент массива может принимать значение 1 (на этом месте посажен цветок) и 0 (пустое место). Существует ограничение - цветы не могут быть посажены на соседних местах.

Необходимо написать функцию, которая определит, можем ли мы посадить в нашу клумбу n новых цветов.

⚠️ Ограничения

- Длина клумбы лежит в диапазоне от 1 до 20000
- Значение каждого элемента массива может равняться либо 0, либо 1
- В исходном массиве не может быть двух цветков на соседних местах (то есть, исходно мы имеем валидный массив)
- n лежит в диапазоне от 0 до длины массива flowerbed (то есть, не превышает размер клумбы)

1️⃣Пример

Входящие данные

flowerbed = [1,0,0,0,1], n = 1


Ответ

true


2️⃣ Пример

Входящие данные

[1,0,0,0,1], n = 2


Ответ

false


Решение

Посмотреть подробное объяснение решения

#array #easy
🎉8🔥3👍21
Неповторяющееся число

Привет, друзья!

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

Сложность: 🟢 Легкая

ℹ️ Описание

Дан непустой массив целых чисел nums, каждый элемент в котором появляется дважды, кроме одного. Найдите этот уникальный элемент.

Вы должны реализовать решение с линейной сложностью по времени и константной по памяти.

⚠️ Ограничения

- В массиве может быть от 1 до 30 000 элементов
- Каждый элемент массива имеет значение в диапазоне от -30 000 до 30 000
- Каждый элемент массива встречается дважды, за исключением одного элемента

1️⃣ Пример

Входящие данные

[2, 2, 1]


Ответ

1


2️⃣ Пример

Входящие данные

[4, 1, 2, 1, 2]


Ответ

4


Решение

Я знаю, что первая идея, которая может прийти вам в голову — это реализовать классический обход массива с подсчетом частоты каждого числа. Для этого для каждого числа в хеш-таблице мы будем хранить пару, в которой ключом является само число, а значением — сколько раз оно встречается в массиве. В конце нужно будет лишь просмотреть всю таблицу и выбрать то число, у которого счетчик равен 1.

В результате мы получим такое решиние.






export const singleNumberMap = (nums: number[]): number => {
const countMap: Record<number, number> = {}

nums.forEach((num) => {
const curr = countMap[num] ?? 0
countMap[num] = curr + 1
})

const entries = Object.entries(countMap)

for (let i = 0; i < entries.length; i++) {
const [num, count] = entries[i]
if (count === 1) {
return Number(num)
}
}

return 0
}


Однако, такое решение нам не подойдет, потому что использование хеш-таблицы для хранения частот приведет к тому, что у нас будет линейная сложность по памяти.

На самом деле задача очень легкая, но надо знать хитрость, а именно — как работает операция XOR.

Посмотреть разбор решения в блоге

#array #easy #bit_manipulation
🔥11👍4👏1🤔1
Разворот гласных в строке

Всем привет. Сегодня пятница и мы разбираем новую задачу.

Сложность: 🟢 Легкая

ℹ️ Описание

Вам дана строка s. Напишите функцию, которая развернёт в ней все гласные и вернет новую строку в качестве результата.

В строке могут встречаться следующие гласные: a, e, i, o, и u как в верхнем, так и в нижнем регистре.

⚠️ Ограничения

— В строке может быть от 1 до 3 * 10^5 символов
— Строка состоит из печатных ASCII символов
— Символы могут быть в верхнем и нижнем регистре

1️⃣ Пример

Входящие данные


hello


Ответ


holle


2️⃣ Пример

Входящие данные


algorithmics


Ответ


ilgirothmacs


3️⃣ Пример

Входящие данные


ab


Ответ


ab


Решение

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


const dict = new Set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'])


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

Теперь нужно реализовать разворот гласных. Для этого заведем два указателя:

leftIdx равен нулю
rightIdx равен индексу последнего символа встроке

Чтобы правильно реализовать разворот, мы будем поочередно двигать левый и правый индексы, пока каждый из них не будет указывать на гласную букву. Как только это произойдет, поменяем буквы местами и будем повторять этот алгоритм до тех пор, пока leftIdx < rightIdx.

Посмотреть реализацию в блоге

#strings #easy
👍5🔥51👏1
Непересекающиеся интервалы

В эту пятницу у нас с вами очередная задача. Приготовьтесь, объяснение ее решения не самое простое.

Сложность: 🟡 Средняя

ℹ️ Описание

Дан массив интервалов intervals, где intervals[i] = [start, end]. Напишите функцию, которая возвращает минимальное количество интервалов, которое вам нужно удалить, чтобы остальные интервалы не пересекались.

⚠️ Ограничения

— Длина массива находится в диапазоне от 1 до 10^5
— Каждый элемент массива также является массивом из двух элементов
— Значения границ интервалов находятся в диапазоне от -5 * 10^4 до 5 * 10^4
— Нижняя граница интервала всегда меньше верхней

1️⃣ Пример

Входящие данные


[[1,2],[2,3],[3,4],[1,3]]


Ответ


1


2️⃣ Пример

Входящие данные


[[1,2],[1,2],[1,2]]


Ответ


2


3️⃣ Пример

Входящие данные


[[1,2],[2,3]]


Ответ


0


Решение

Рассмотрим два интервала с самым ранним временем окончания. Допустим, более раннее время окончания — x, а более позднее — y, то есть х < у.

Если мы можем сохранить только один интервал, следует ли нам выбрать тот, который заканчивается на «x» или тот, который оканчивается на «y»? Чтобы избежать дублирования, мы всегда должны жадно выбирать интервал с более ранним временем окончания «x». Логику, стоящую за этим, можно описать следующим образом:

— Мы выбираем либо x, либо y. Назовем наш выбор k.
— Чтобы избежать пересечения, время начала следующего интервала, который мы выбираем, должно быть больше или равно k.
— Мы хотим максимизировать интервалы, которые мы используем (без пересечения), поэтому мы хотим максимизировать выбор для следующего интервала.
— Поскольку время начала следующего интервала должно быть больше или равно k, большее значение k никогда не сможет дать нам больше выбора, чем меньшее значение k.
— Таким образом, мы должны попытаться минимизировать k. Следовательно, нам всегда следует жадно выбирать x, поскольку x < y.

В общем, k равно времени окончания самого последнего сохраненного нами интервала.

Решение задачи мы начинаем с сортировки интервалов по времени окончания, чтобы мы могли обрабатывать их по порядку.
Поскольку мы отсортировали интервалы по времени окончания, «y» должно быть больше «k». Это дает нам два возможных варианта:

— Вариант 1, x >= k: мы можем смело использовать этот интервал, поскольку он не приведет к пересечению.
— Вариант 2, x < k: использование этого интервала приведет к пересечению. Как мы установили ранее, нам всегда следует брать интервалы с более ранним временем окончания. Поскольку y > k, мы должны удалить текущий интервал.

Посмотреть реализацию в блоге

🅾️ Оценка сложности

По времени

Для того чтобы найти ответ, нам достаточно один раз пройтись по исходному массиву со сложностью O(n). Однако предварительно мы еще делаем сортировку массива, сложность которой скорее всего равна O(n⋅logn).

Поэтому итоговая сложность по времени равна O(n⋅logn).

По памяти

Мы не создаем переменных, зависящих от длины входных данных, однако для сортировки также требуется выделение памяти. В разных языках используются разные алгоритмы, поэтому сложность по памяти будет варьироваться от O(logn) до O(n).

#arrays #medium
4👍2🔥2
Плюс один

Для решения сегодняшней задачи мы с вами вспомним школьную программу, а именно — как складывать числа столбиком.

Сложность: 🟢 Легкая

ℹ️ Описание

Дано большое целое число, представленное в виде целочисленного массива digits, где digits[i] — это i-я цифра целого числа. Цифры упорядочены от наиболее значимого к наименее значимому, слева направо. Число не содержит ведущих нулей.
Увеличьте число на единицу и верните полученный массив цифр.

⚠️ Ограничения

— В массиве цифр может быть от 1 до 100 элементов
— В массиве содержатся только цифры от 0 до 9
— Число не содержит ведущих нулей

1️⃣ Пример

Входящие данные


Ответ



2️⃣ Пример

Входящие данные


Ответ



3️⃣ Пример

Входящие данные


Ответ



Решение

Чтобы решить задачу, нужно реализовать подход со сложением чисел в столбик.
Каждая цифра в массиве представляет собой один разряд числа. Сначала нужно прибавить к младшему разряду (крайнему справа) единицу согласно условиям задачи, а потом следовать простому алгоритму.

1. Если сумма меньше 10, то вместо текущего разряда нужно записать эту сумму.

2. Если сумма больше 10, то вместо текущего разряда нужно записать остаток от деления суммы на 10, а к следующему разряду прибавить единицу. Для всех последующих разрядов нужно повторить эти же действия.

3. Если мы обрабатываем старший разряд (крайний слева) и сумма больше 10, то в текущий разряд мы добавляем остаток от деления суммы на 10 и добавляем еще один разряд, в который записываем единицу.

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

Посмотреть реализацию в блоге

🅾️ Оценка сложности

По времени

O(n) — так как мы дважды итерируемся по всему массиву.

По памяти

O(n) — так как мы выделяем память для хранения результирующего массива.

#arrays #easy #math
👍4🔥41
Разворот слов в строке

Подобные задачи чаще всего попадаются на собеседованиях — на первый взгляд задача кажется очень простой, особенно если воспользоваться встроенными функциями языка - trim, split и так далее - задача решается в несколько строчек кода.
Но, обычно, в таких задачах интервьеры просят НЕ использовать эти функции и реализовать все самим. И в этот момент частенько «зависаешь» на реализации.

Сложность: 🟡 Средняя

ℹ️ Описание

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

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

⚠️ Ограничения

— Длина строки может быть в диапазоне от 1 до 10000
— Строка содержит только буквы латинского алфавита, числа и пробелы.
— В каждой строке есть как минимум одно слово


1️⃣ Пример

Входящие данные

«the sky is blue»

Ответ

«blue is sky the»


2️⃣ Пример

Входящие данные

« hello world »

Ответ

«world hello»


3️⃣ Пример

Входящие данные

«a good example»

Ответ

«example good a»


Решение

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

«a good example».


1. Удаляем лишние пробелы внутри строки и получаем строку

«a good example»


2. Далее переворачиваем каждое слово внутри строки и получаем строку

«a doog elpmaxe»


3. Последним шагом переворачиваем всю строку и получаем требуемый результат

«example good a»


Посмотреть реализацию в блоге

🅾️ Оценка сложности

По времени

O(n) — так как мы несколько раз итерируемся по всей строке.

По памяти

O(n) — так как мы выделяем память для работы с новой строкой.

#strings #medium
🔥5👍2👏1
🎉 Нас уже больше 1000! 🎉

Мы очень рады, что наши разборы задач интересны и полезны такому большому количеству людей.
Спасибо, что читаете нас!

Мы стараемся для вас ❤️
Please open Telegram to view this post
VIEW IN TELEGRAM
15🍾6🥰4👏1
Декодирование строки

Сложность: 🟡 Средняя

ℹ️ Описание

Вам дана закодированная строка, верните ее декодированную версию.

В строке используется следующее правило кодирования: k[encoded_string], означает что закодированная внутри квадратных скобок строка должна повторяться ровно k раз. Обратите внимание, что k гарантированно является положительным целым числом.

При этом входная строка всегда гарантированно валидная, то есть в ней нет лишних пробелов и квадратные скобки имеют правильную форму. Кроме того, цифры в строке предназначены только для повторяющихся чисел k. Например, не будет ввода типа 3a или 2[4].

Обратите внимание на то, что закодированные строки могут вкладываться друг в друга.

⚠️ Ограничения

— Длина входной строки находится в диапазоне от 1 до 30
— Входная строка содержит только латинские буквы в нижнем регистре, цифры и квадратные скобки
— Входная строка всегда валидная
— Значения k находятся в диапазоне от 1 до 300
— Длина результирующей строки не превышает 10^5


1️⃣ Пример

Входящие данные


"3[a]2[bc]"


Ответ


"aaabcbc"


2️⃣ Пример

Входящие данные


"3[a2[c]]"


Ответ


"accaccacc"


3️⃣ Пример

Входящие данные


"2[abc]3[cd]ef"


Ответ


"abcabccdcdcdef"


Решение

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

1. Создаем результирующую пустую строку, которая будет использоваться для накопления декодированного результата.

2. Далее перебираем строку посимвольно и проверяем следующие условия:
- Если текущий символ является буквой (от ‘a’ до ‘z’), добавляем его в результат res и переходим к следующему символу.
- Если встречается цифра, это означает начало закодированного блока. Число перед [ (количество повторений) считывается посимвольно. Так как k может быть многозначным числом, используется накопление count в цикле, где каждая новая цифра добавляется к count с учетом ее разрядности (умножение на 10).

3. После определения count и нахождения открывающей скобки [, алгоритм ищет соответствующую закрывающую скобку ]. Это делается с помощью счетчика bracket, который увеличивается при нахождении [ и уменьшается при нахождении ], позволяя обрабатывать вложенные скобки.

4. Как только найдена соответствующая закрывающая скобка, вырезается подстрока между [ и ] и для нее рекурсивно вызывается функция decodeString. Результат этого вызова повторяется count раз и добавляется к итоговому результату res.

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

Посмотреть реализацию в блоге

🅾️ Оценка сложности

По времени

O(maxk * n)
, где maxk — максимальное значение k, а n — длина данной строки s.

По памяти

O(n) — это пространство, используемое для хранения внутреннего стека вызовов для рекурсии. Поскольку мы рекурсивно декодируем каждый вложенный шаблон, максимальная глубина стека рекурсивных вызовов не будет превышать n.

#strings #medium
👍6🔥21🙏1
Dota2 Senate

Данная задача относится к моему «любимому» типу — попробуй пойми что от тебя хотят.

Первая сложность задачи не в поиске самого решения, а в попытках сократить контекст и описания с половины экрана до нескольких строчек, убирая весь лор про Dota2, сенаторов и регламенты проведения голосований.
Безусловно, в реальных условиях так часто и бывает — вам приносят бизнес задачу/проблему, которую вы должны решить, а не готовое ТЗ, где все структурировано расписано на понятном вам языке. Но для задач на собеседовании это перебор. Время сильно ограничено, вы стрессуете и вместо того, чтобы думать над задачей - пытаетесь продраться через витиеватое описание.

Сложность: 🟡 Средняя

ℹ️ Описание

В мире Dota2 существует 2 партии Radiant и Dire.
Для того чтобы внести изменение в игру Dota2 необходимо провести голосование сената, каждый член которого принадлежит одной из двух партий.

Голосование идет раундами, в каждом раунде по очереди опрашивается каждый сенатор.
Сенатор может выполнить одно из двух действий:
- лишить следующего сенатора из противоположной партии права голоса;
- объявить победу своей партии, если не осталось сенаторов с правом голоса из противоположной партии.
Раунд начинается с крайнего левого сенатора.

Напишите функцию, которая будет рассчитывать результаты голосования сената.

⚠️ Ограничения

- Количество сенаторов (длина входящей строки) находится в диапазоне от 1 до 10000
- Входящая строка состоит из символов R и D


1️⃣ Пример

Входящие данные

RD

Ответ

Radiant


2️⃣ Пример

Входящие данные

RDD

Ответ

Dire



Решение

На первый взгляд задача может решиться простым сравнением количества сенаторов из каждой партии.
На самом деле такое решение будет не верно, так как не учитывает порядок голосования. Это легко понять на примере RDRDRDRDRDDD.

Правильный подход для решения этой задачи — воспользоваться структурой данных «очередь», поместив в нее сенаторов в заданом порядке.
Тогда весь процесс сведется к двум действиям:
- Забрать из начала очереди сенатора, а после того как тот совершит ход (лешит права голоса ближайшего сенатора из противоположной партии), помещать его в конец очереди.
- Удалить из очереди сенаторов без права голоса.

Итоговый ответ мы получим, когда в очереди останутся представители только одной партии.

Посмотреть реализацию и подробный разбор примера в блоге

🅾️ Оценка сложности

По времени

O(n) — так как нам придется несколько раз проитерироваться по очереди.

По памяти

O(n) — так как мы выделяем память для работы с очередью.

#queue #medium
👍10
Самый низкий общий предок двоичного дерева

Сложность: 🟡 Средняя

ℹ️ Описание

Вам дано двоичное дерево. Найдите наименьшего общего предка (LCA) двух заданных узлов в дереве.

Согласно определению LCA в Википедии: «Наименьший общий предок определяется между двумя узлами p и q как самый нижний узел в дереве T, который имеет как p, так и q в качестве потомков (где мы позволяем узлу быть потомком самого себя).»

⚠️ Ограничения

— Количество узлов в дереве находится в диапазоне от 2 до 10^5.
— Значения каждого узла в дереве находятся в диапазоне от -10^9 до 10^9.
— Значения всех узлов в дереве уникальны.
— p != q.
— p и q всегда существуют в дереве.


1️⃣ Пример

Входные параметры: дерево выше, p = 5, q = 1.

Ответ: 3

Объяснение: LCA узлов 5 и 1 равен 3.

2️⃣ Пример

Входные параметры: дерево выше, p = 5, q = 4.

Ответ: 5

Объяснение: LCA узлов 5 и 4 равен 5, поскольку узел может быть потомком самого себя согласно определению LCA.


Решение

Решение этой задачи достаточно интуитивно. Сначала мы пройдем по дереву вглубь. В тот момент, когда встретится любой из узлов p или q, вернем логический флаг. Флаг помогает определить, нашли ли мы нужные узлы на каком-либо из путей. Тогда наименьшим общим предком будет узел, для которого обе рекурсии поддерева возвращают флаг true. Это также может быть узел, который сам является одним из узлов p или q и для которого одна из рекурсий поддерева возвращает флаг true.

Для реализации будем использовать рекурсивный подход с замыканием для хранения переменной lca.

Посмотреть реализацию в блоге

🅾️ Оценка сложности

По времени

O(n) — так как в худшем случае нам нужно посетить все n узлов в дереве.

По памяти

O(n) — так как максимальный объем пространства, используемого стеком рекурсии, будет равен n.

#tries #medium
🔥5👍42
Угадай число

Сегодня мы рассмотрим вместе с вами еще одну классическую задачу из учебников.

Сложность: 🟢 Легкая

ℹ️ Описание

Мы играем в игру, где вы должны угадать загаданное число.
Игра заключается в следующем.

— Я выбираю число от 1 до n.
— Вы должны угадать, какое число я выбрал.
— Каждый раз, когда вы угадаете неправильно, я скажу вам, больше или меньше выбранное мной число, чем ваше предположение.

Вы можете использовать предоставленный метод guess для проверки догадки со следующей сигнатурой.

int guess(int num)

Метод возвращает следующие результаты:

— -1 если ваша догадка больше, чем выбранное мной число;
— 1 если ваша догадка меньше выбранного мной числа;
— 0 если ваша догадка равна выбранному мною числу.

Угадайте число, которое я загадал.

⚠️ Ограничения

Загадываемое число всегда находится в диапазоне от 1 до 2^31 - 1

1️⃣ Пример

Исходные данные: n = 10, загадано число 6

Ответ:
6

2️⃣ Пример

Исходные данные: n = 1, загадано число 1

Ответ:
1

3️⃣ Пример

Исходные данные: n = 2, загадано число 1

Ответ: 1

Решение

Это классическая задача, которая решается при помощи бинарного поиска.

Нам нужно итеративно выполнять несколько шагов.

1. Найти крайнюю левую и правую границы интервала, то есть 1 и n соответственно.
2. Найти середину этого интервала и проверить полученное число:
— если полученное число больше загаданного, то правую границу нужно сместить на mid - 1;
— если полученное число меньше загаданного, то левую границу нужно сместить на mid + 1;
— если полученное число равно загаданному, то мы нашли ответ.

Этот алгоритм нужно повторять до тех пор, пока мы не найдем ответ или пока левая и правая границы не пересекутся.

Посмотреть реализацию в блоге

🅾️ Оценка сложности

По времени

O(log n)
так как мы перебираем диапазон чисел бинарным поиском.

По памяти

0(1) так как мы не выделяем дополнительной памяти.

#binary_search
👍74🔥2
Количество последних вызовов

Сложность: 🟢 Легкая

ℹ️ Описание

Вам дан класс RecentCounter, который подсчитывает количество последних вызовов за определенный период времени. Реализуйте этот класс.

Конструктор RecentCounter инициализирует счетчик с нулевым количеством последних вызовов.
Класс имеет м
етод ping, который принимает в качестве аргумента параметр t (время в миллисекундах последнего вызова) и в качестве ответа возвращает количество вызовов, произошедших за последние 3000 мс.

Гарантируется, что каждый вызов ping использует строго большее значение t, чем предыдущий вызов.

⚠️ Ограничения

— Значение t находится в диапазоне от 1 до 10^9
— Каждый пример будет вызывать ping со строго возрастающими значениями t
— Для проверки будет совершено не более 10^4 вызовов метода ping

1️⃣ Пример

```golang



counter := Constructor()

counter.Ping(1)
counter.Ping(100)
counter.Ping(3001)
counter.Ping(3002)
counter.Ping(3003)
```

Ответ:
4

Пояснение: За последние 3000 мс было сделано 4 вызова в следующее время [100, 3001, 3002, 3003].

2️⃣ Пример






counter := Constructor()

counter.Ping(1)
counter.Ping(100)
counter.Ping(3001)


Ответ:
3

Пояснение: За последние 3000 мс было сделано 4 вызова в следующее время [1, 100, 3001].

Решение

Эта задача решается буквально в несколько строчек.

Для хранения вызовов определим массив calls внутри класса, который при инициализации экземпляра получает значение пустого массива.

Далее в реализации метода ping сначала надо добавить время нового вызова в массив calls, а потом удалить из начала все элементы, которые вываливаются из интервала в 3000 миллисекунд, тем самым реализовав простую очередь. В конце нужно лишь вернуть длину оставшегося массива.

Посмотреть реализацию в блоге

🅾️ Оценка сложности

По времени

Основная временная сложность нашего метода ping заключается в цикле, который в худшем случае будет выполнять 3000 итераций для извлечения всех устаревших элементов, а в лучшем случае — одну итерацию. Исходя из этого сложность равна O(3000) = O(1).

По памяти

Сложность O(1), так как максимальная длина нашего массива вызовов — 3000 элементов. По условию задачи, каждое новое значение в нем является целым числом и строго больше предыдущего, поэтому мы точно можем определить максимальный размер массива.

#queue #easy
3👍1🔥1
Максимальный зигзагообразный путь в бинарном дереве

Продолжаем закреплять тему деревьев. В общем виде, если вы встречаете задачу на деревья или графы, с большой долей вероятности стоит вспоминать и модифицировать поиск в ширину/глубину.

Сложность: 🟡 Средняя

ℹ️ Описание

Напишите функцию, которая будет вычислять длину самого длинного зигзагообразного пути в бинарном дереве. Путь может начинаться НЕ с корневого элемента дерева.

⚠️ Ограничения

Количество элементов в дереве от 1 до 50000


Решение

Для решение задачи нам потребуется вспомнить как работает алгоритм поиска в глубину и немного модифицировать его. Основная сложность данной задачи в том, что наш путь не обязан начинаться от корня дерева, он может «всплывать» из более глубоких слоев дерева, поэтому на каждой ноде нам нужно оперировать четырьмя величинами:

- Длиной пути, начинающегося от текущего элемента направо.
- Длиной пути, начинающегося от текущего элемента налево.
- Длиной максимального пути «снизу» с левой дочерней ноды.
- Длиной максимального пути «снизу» с правой дочерней ноды.

Поднимаясь по слоям дерева во время рекурсивного обхода нам будет достаточно вычислять и возвращать максимум из этих величин.

Посмотреть примеры и реализацию в блоге

🅾️ Оценка сложности

По времени

O(n) —так как нам нужно совершить поиск в глубину по бинарному дереву, перебрав все n его элементов.

По памяти

O(n) — так как мы используем рекурсивный подход. Это означает, что мы будем хранить в памяти n переменных во время вычисления стека рекурсии.

#tries #medium
1👍1
console.log(`
__
_ / /|
|\\ \/_/
\_\| / __
\/_/__\ .-=='/~\
____,__/__,_____,______)/ /{~}}}
-,------,----,-----,---,\'-' {{~}}
jgs '-==.\}/
`)


Сегодня не будет задач!

Cегодня мы поздравляем всех наших подписчиц с прекрасным женским днем.
Будьте красивыми, счастливыми и прокаченными в алгоритмах!

С праздником! 🎉
Please open Telegram to view this post
VIEW IN TELEGRAM
💋6👍32🔥1🤔1💔1
Переместите нули

Сложность: 🟢 Легкая

ℹ️ Описание

Вам дан целочисленный массив nums. Переместите в нём все нули в конец, сохраняя относительный порядок ненулевых элементов.

Обратите внимание, что вы должны сделать это in-place, не копируя массив.

⚠️ Ограничения

— Длина массива от 1 до 10000 элементов
— Каждый элемент массива может принимать значение в диапазоне от -2^31 до 2^31 - 1

1️⃣ Пример

Входные данные:


nums = [0,1,0,3,12]

Ответ


[1,3,12,0,0]

2️⃣ Пример

Входные данные:

nums = [0]

Ответ


[0]

Решение

Решение задачи крайне простое.

Нам нужно завести два индекса. Первый — обычный индекс i, который инкриминируется на каждой итерации. Второй firstZeroIndex — индекс первого нулевого элемента в массиве, который изначально равен 0.

Далее мы итерируемся по всем элементам массива и, как только встречаем ненулевой элемент, меняем его местами с нулем, который стоит под индексом firstZeroIndex. Дополнительно увеличиваем firstZeroIndex, если произошла перестановка.

Таким образом все нули будут «всплывать» в конец массива за один проход..

Посмотреть реализацию в блоге

🅾️ Оценка сложности

По времени

Сложность O(n), так как мы итерируемся по всем элементам массива.

По памяти

Сложность O(1), так как мы не выделяем дополнительную память, зависящую от длины массива.

#arrays #easy
👍4🔥21
Дети с наибольшим количеством конфет

Сложность: 🟢 Легкая

ℹ️ Описание

Есть n детей с конфетами.

Вам дан целочисленный массив candies, где candies[i] представляет количество конфет, которые есть у i-го ребенка, и целое число extraCandies, обозначающее количество дополнительных конфет, которые у вас есть.

Верните массив result длины n, где result[i] имеет значение true, если после предоставления i-му ребенку всех дополнительных конфет у него будет наибольшее количество конфет среди всех детей, или false в противном случае.

Обратите внимание, что несколько детей могут получить наибольшее количество конфет одновременно.

⚠️ Ограничения

— Длина массива candies находится в диапазоне от 1 до 100
— У каждого ребенка может быть не менее 1 и не более 100 конфет
— Значение extraCandies находится в диапазоне от 1 до 50

1️⃣ Пример

Входные
данные:

candies = [2,3,5,1,3]
extraCandies = 3

Ответ

[true,true,true,false,true]

2️⃣ Пример

Входные дан
ные:

candies = [4,2,1,1,2]
extraCandies = 1

Ответ


[true,false,false,false,false]

Решение

Для решения задачи мы можем найти максимальное количество конфет среди всех детей и затем проверить, превысит ли количество конфет у конкретного ребенка максимум, если к его количеству конфет добавить extraCandies.

Посмотреть реализацию в блоге

🅾️ Оценка сложности

По времени

Для того чтобы найти ответ, мы дважды итерируемся по массиву, то есть совершаем 2n операций. Итоговая сложность алгоритма равна O(n).

По памяти

Сложность по памяти линейная O(n), так как мы создаем массив длиной n для хранения ответа.

#arrays #easy
1🔥1👏1
Определите, близки ли две строки

Сложность: 🟡 Средняя

ℹ️ Описание

Вам дано две строки. Верните true, если обе строки являются близкими, и false в противном случае.

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

Операция 1: Поменяйте местами любые два существующих символа (swap). Например, abcde -> aecdb (букву b поменяли местами с буквой e).

Операция 2: Преобразуйте каждое появление одного существующего символа в другой существующий символ и сделайте то же самое с другим символом. Например, aacabb -> bbcbaa (все буквы a превращаются в буквы b, а все буквы b превращаются в буквы a).

Вы можете использовать операции с любой строкой столько раз, сколько необходимо.

⚠️ Ограничения

— Длина каждого слова находится в диапазоне от 1 до 10^5
— Оба слова содержат только строчные буквы латинского алфавита

1️⃣ Пример

Входные данные:
word1 = "abc", word2 = "bca"

Ответ:
true

Объяснение

Вы можете получить word1 из word2 за 2 операции.

Примените операцию 1: «abc» -> «acb»
Примените операцию 1: «acb» -> «bca»

2️⃣ Пример

Входные данные:
word1 = "a", word2 = "aa"

Ответ:
false

Объяснение

Невозможно получить word2 из word1 или наоборот за любое количество операций.

3️⃣ Пример

Входные данные:
word1 = "cabbba", word2 = "abbccc"

Ответ:
true

Объяснение

Вы можете получить word1 из `word2 за 3 операции.

Примените операцию 1: «cabbba» -> «caabbb»
Примените операцию 2: «caabbb» -> «baaccc»
Примените операцию 2: «baaccc» -> «abbccc»

Решение

Посмотреть решение в блоге

#strings #medium
🔥6😁21👏1🤡1
Разворот связанного списка

Привет, друзья. В этот вторник хочется решить что-то новое, но ненапряжное, поэтому давайте расмотрим задачу на связанные списки. С ними мы еще не работали.

Сложность: 🟢 Легкая

ℹ️ Описание

Вам дан односвязный список. Переверните список и верните его.

Список представлен следующей структурой.


class ListNode {
val: number
next: ListNode | null
}


⚠️ Ограничения

— Количество узлов в связанном списке находится в диапазоне от 1 до 5000
— Значение каждого узла находится в диапазоне от -5000 до 5000

Решение

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

Посмотреть реализацию в блоге

🅾️ Оценка сложности

По времени

Сложность O(n), так как мы итерируемся по всем узлам списка.

По памяти

Сложность O(1), так как мы не выделяем дополнительную память.

#linked_list #easy
👍4🔥21