C# | LeetCode
3.48K subscribers
159 photos
1 file
1.06K links
Cайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.iss.one/+nebTPWgpeGs1OWFi
Вопросы собесов t.iss.one/+sjKGQXl79ytkYzIy
Вакансии t.iss.one/+BQFHXZQ0zrViNGIy
Download Telegram
Задача: 23. Merge k Sorted Lists #hard
Условие:
Вам дан массив из k списков связанных списков, каждый связанный список отсортирован в порядке возрастания.

Объедините все связанные списки в один отсортированный связанный список и верните его.

Решение:
public class Solution {
public ListNode MergeKLists(ListNode[] lists)
{
if (lists == null || lists.Length == 0)
return null;

return Merge(lists, 0, lists.Length - 1);
}

private ListNode Merge(ListNode[] lists, int i, int j)
{
if (j == i)
return lists[i];
else
{
int mid = i + (j - i) / 2;

ListNode left = Merge(lists, i, mid),
right = Merge(lists, mid + 1, j);

return Merge(left, right);
}
}

private ListNode Merge(ListNode list1, ListNode list2)
{
ListNode dummy = new ListNode(0),
cur = dummy;

while (list1 != null && list2 != null)
{
if (list1.val <= list2.val)
{
cur.next = list1;
list1 = list1.next;
}
else
{
cur.next = list2;
list2 = list2.next;
}

cur = cur.next;
}

if (list1 != null)
cur.next = list1;

if (list2 != null)
cur.next = list2;

return dummy.next;
}
}

Пояснение:
Данный код представляет собой класс `Solution`, в котором содержатся методы для объединения нескольких отсортированных списков в один общий отсортированный список. Давайте разберем пошагово, что происходит в каждом из методов:

1. Метод `MergeKLists` принимает массив списков `lists` и возвращает объединенный список. Если входной массив `lists` равен `null` или его длина равна 0, то метод вернет `null`. В противном случае метод вызывает вспомогательный метод `Merge`, передавая ему массив `lists`, начальный индекс 0 и конечный индекс `lists.Length - 1`.

2. В методе `Merge` осуществляется проверка условия, при котором начальный индекс `i` равен конечному индексу `j`. Если это условие выполняется, метод возвращает список из массива `lists` по индексу `i`. В противном случае происходит разбиение массива на две части посредством вычисления среднего индекса `mid`. Затем метод рекурсивно вызывает себя для левой половины массива и для правой половины массива, получая два подсписка `left` и `right`. Затем вызывается отдельный метод `Merge`, который объединяет два подсписка в один.

3. Метод `Merge` принимает два списка `list1` и `list2` и создает фиктивный узел `dummy`. При помощи указателя `cur` осуществляется сравнение элементов `val` двух списков и добавление их в новый список в отсортированном порядке. Таким образом, метод просматривает оба списка `list1` и `list2`, сравнивая и перенаправляя указатель на следующий минимальный элемент. П

осле окончания цикла, оставшиеся элементы списка добавляются в конец нового списка.

В итоге, данный код реализует эффективный способ объединения нескольких отсортированных списков в один общий отсортированный список, используя подход "разделяй и властвуй" и пространственную сложность O(1).
👍1
Задача: 24. Swap Nodes in Pairs #medium
Условие:
Учитывая связанный список, поменяйте местами каждые два соседних узла и верните его голову. Вы должны решить проблему, не изменяя значения в узлах списка (т. е. изменять можно только сами узлы).

Решение:
 public ListNode SwapPairs(ListNode head)
{
if (head == null)
return head;

ListNode s = head;
ListNode h1 = head;
ListNode h2 = head.next;

while (h2 != null)
{
//SWAP
var t = h1.val;
h1.val = h2.val;
h2.val = t;


if (h2.next == null || h1.next.next == null)
break;

// move by +2
h2 = h2.next.next;
h1 = h1.next.next;
}
return s;
}

Пояснение:
Данный код реализует функцию SwapPairs для обмена парами значений в узлах односвязного списка. Вот краткое объяснение кода:

1. Проверка наличия списка: Если голова списка head равна null, то функция возвращает head без изменений.

2. Инициализация переменных: Создаются три указателя на узлы - s (хранит ссылку на начало списка), h1 (указывает на текущий узел) и h2 (указывает на следующий узел после h1).

3. Цикл обмена парами: Пока h2 не станет равным null (то есть список содержит хотя бы два узла), выполняется следующее:
- Значения узлов h1 и h2 меняются местами.
- Выполняется проверка на окончание списка: если
h2.next равен null или h1.next.next равен null (то есть остался только один узел или ни одного), цикл прерывается.
- Указатели h1 и h2 смещаются на два узла вперед, чтобы перейти к следующей паре узлов.

4. Возвращение начальной головы: После обмена всех попарных значений в узлах функция возвращает начальную голову списка s.

Функция SwapPairs попарно обменивает значения узлов в односвязном списке до конца списка, сохраняя ссылку на начало списка.
👍1
Задача: 25. Reverse Nodes in k-Group #hard
Условие:
Учитывая заголовок связанного списка, поменяйте местами узлы списка k за раз и верните измененный список.

k — целое положительное число, меньшее или равное длине связанного списка. Если количество узлов не кратно k, то пропущенные узлы в конечном итоге должны остаться такими, какие они есть.

Вы не можете изменять значения в узлах списка, можно изменять только сами узлы.

Решение:
    public class Solution
{
//https://leetcode.com/problems/reverse-linked-list-ii/
private void ReverseList(ListNode head, int m, int n, out ListNode groupHead)
{
var curr = head.next;
var inputHead = head;
int count = n - m;
int idx = 0;

while (idx < count)
{
inputHead.next = curr.next;
curr.next = head;
head = curr;
curr = inputHead.next;
idx++;
}

groupHead = head;
}

public ListNode ReverseKGroup(ListNode head, int k)
{
if (head == null)
{
return null;
}

int count = 0;
ListNode curr = head;
while (curr != null)
{
count++;
curr = curr.next;
}


ListNode res = null;
ListNode prev = null;
curr = head;
int idx = 0;

while (true)
{
if (idx + k - 1 >= count)
{
res = res ?? head;
break;
}

ReverseList(curr, idx, idx + k - 1, out ListNode groupHead);
curr = groupHead;
if (prev != null)
{
prev.next = groupHead;
}

for (int i = 0; i < k; i++)
{
prev = curr;
curr = curr.next;
}

res = res ?? groupHead;
idx += k;
}

return res;
}
}

Пояснение:
1. ReverseList: Это приватный метод, который реализует обращение части связанного списка между позициями m и n. Он обновляет указатель головы группы groupHead, пройдя по списку и меняя указатели в нужной последовательности.

2. ReverseKGroup: Это публичный метод, который реверсирует группы узлов по k элементов в связанном списке. Он сначала определяет общее количество узлов в списке, затем поочередно вызывает ReverseList для групп узлов длиной k. После завершения обработки всех групп он возвращает новую голову списка.

Общий подход заключается в том, что ReverseList изменяет указатели узлов для обращения части списка, а ReverseKGroup последовательно применяет ReverseList для групп узлов длиной k и обновляет указатели в соответствии с этим. Код решает задачу обращения групп узлов в связанном списке.
👍3
Задача: 26. Remove Duplicates from Sorted Array #easy
Условие:
Учитывая целочисленный массив чисел, отсортированный в неубывающем порядке, удалите дубликаты на месте так, чтобы каждый уникальный элемент появлялся только один раз. Относительный порядок элементов должен оставаться неизменным. Затем верните количество уникальных элементов в числах.

Считайте, что количество уникальных элементов чисел равно k. Чтобы вас приняли, вам нужно сделать следующее:

Измените массив nums так, чтобы первые k элементов nums содержали уникальные элементы в том порядке, в котором они присутствовали в nums изначально. Остальные элементы nums не важны, как и размер nums.
Вернуть К.

Решение:
public class Solution {
public int RemoveDuplicates(int[] nums) {
if (nums == null || nums.Length == 0)
return 0;

int i = 0;

for (int j = 0; j < nums.Length; j++)
{
nums[i++] = nums[j];

while (j < nums.Length - 1 && nums[j] == nums[j + 1])
j++;
}

return i;
}
}

Пояснение:
Данный код представляет собой класс `Solution`, в котором содержится метод `RemoveDuplicates`, предназначенный для удаления повторяющихся элементов в упорядоченном массиве `nums`. Вот пояснение к данному методу:

1. Метод `RemoveDuplicates` принимает входной массив `nums` и возвращает целое число, представляющее количество уникальных элементов в массиве после удаления повторяющихся. Если входной массив `nums` равен `null` или его длина равна 0, метод возвращает 0, так как в этом случае массив уже не содержит уникальных элементов и нечего удалять.

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

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

4. В теле цикла while проверяется, если текущий элемент `nums[j]` равен следующему элементу `nums[j + 1]` и если `j` не указывает на последний элемент массива, то индекс `j` увеличивается. Это позволяет пропустить все повторяющиеся элементы и перейти к следующему уникальному элементу.

5. В конце метод возвращает значение `i` после завершения всех итераций, представляющее количество уникальных элементов, которые остались в массиве после удаления повторяющихся. Таким образом, код эффективно удаляет дубликаты из упорядоченного массива, сохраняя порядок оставшихся уникальных элементов.
👍1
Задача: 27. Remove Element #easy
Условие:
Учитывая целочисленный массив nums и целочисленное значение, удалите все вхождения val в nums на месте. Порядок элементов может быть изменен. Затем верните количество элементов в виде чисел, которые не равны val.

Учитывайте количество элементов в nums, которые не равны val be k. Чтобы вас приняли, вам необходимо сделать следующее:

Измените массив nums так, чтобы первые k элементов nums содержали элементы, не равные val. Остальные элементы nums не важны, как и размер nums.
Вернуть К.

Решение:
public class Solution {
public int RemoveElement(int[] nums, int val) {
if (nums == null || nums.Length == 0)
return 0;

int i = 0;

for (int j = 0; j < nums.Length; j++)
{
while (j < nums.Length && nums[j] == val)
j++;

if (j < nums.Length)
nums[i++] = nums[j];
}

return i;
}
}

Пояснение:
Приведенное объяснение верно для данного метода `RemoveElement`. В данном методе алгоритм работает следующим образом:
1. Проверяем, что массив `nums` не равен `null` и его длина не равна 0. Если это так, возвращаем 0.
2. Создаем переменную `i`, которая будет отслеживать позицию для вставки уникальных элементов без значения `val`.
3. Запускаем цикл `for` для перебора всех элементов массива `nums` с помощью индекса `j`.
4. В цикле проверяем, если текущий элемент `nums[j]` равен значению `val`, увеличиваем индекс `j`, пока не найдем следующий элемент, отличный от `val` или не достигнем конца массива.
5. Когда найден уникальный элемент (не равный `val`), копируем его в массив на позицию `i` и увеличиваем `i`.
6. В конце метод возвращает значение `i`, которое представляет новую длину массива после удаления всех элементов со значением `val`.

Этот метод позволяет удалять все экземпляры указанного значения из массива, сохраняя порядок оставшихся элементов. Он эффективно обрабатывает случаи, когда нужно удалить конкретное значение из массива и вернуть новую длину массива после удаления.
Задача: 27. Remove Element #easy
Условие:
Учитывая целочисленный массив nums и целочисленное значение, удалите все вхождения val в nums на месте. Порядок элементов может быть изменен. Затем верните количество элементов в виде чисел, которые не равны val.

Учитывайте количество элементов в nums, которые не равны val be k. Чтобы вас приняли, вам необходимо сделать следующее:

Измените массив nums так, чтобы первые k элементов nums содержали элементы, не равные val. Остальные элементы nums не важны, как и размер nums.
Вернуть К.

Решение:
public class Solution {
public int RemoveElement(int[] nums, int val) {
if (nums == null || nums.Length == 0)
return 0;

int i = 0;

for (int j = 0; j < nums.Length; j++)
{
while (j < nums.Length && nums[j] == val)
j++;

if (j < nums.Length)
nums[i++] = nums[j];
}

return i;
}
}

Пояснение:
Приведенное объяснение верно для данного метода `RemoveElement`. В данном методе алгоритм работает следующим образом:
1. Проверяем, что массив `nums` не равен `null` и его длина не равна 0. Если это так, возвращаем 0.
2. Создаем переменную `i`, которая будет отслеживать позицию для вставки уникальных элементов без значения `val`.
3. Запускаем цикл `for` для перебора всех элементов массива `nums` с помощью индекса `j`.
4. В цикле проверяем, если текущий элемент `nums[j]` равен значению `val`, увеличиваем индекс `j`, пока не найдем следующий элемент, отличный от `val` или не достигнем конца массива.
5. Когда найден уникальный элемент (не равный `val`), копируем его в массив на позицию `i` и увеличиваем `i`.
6. В конце метод возвращает значение `i`, которое представляет новую длину массива после удаления всех элементов со значением `val`.

Этот метод позволяет удалять все экземпляры указанного значения из массива, сохраняя порядок оставшихся элементов. Он эффективно обрабатывает случаи, когда нужно удалить конкретное значение из массива и вернуть новую длину массива после удаления.
👍1
Задача: 28. Find the Index of the First Occurrence in a String #easy
Условие:
Учитывая две строки, игла и стог сена, верните индекс первого вхождения иглы в стоге сена или -1, если игла не является частью стога сена.

Решение:
public class Solution {
public int StrStr(string haystack, string needle) {
int i = 0,
j = 0;
int[] lps = BuildLPS(needle);

while (i < haystack.Length && j < needle.Length)
if (haystack[i] == needle[j])
{
i++;
j++;
}
else
{
if (j > 0)
j = lps[j - 1];
else
i++;
}

return j == needle.Length ? i - j : -1;
}

private int[] BuildLPS(string needle)
{
int[] lps = new int[needle.Length];
int i = 0;

for (int j = 1; j < needle.Length;)
if (needle[i] == needle[j])
{
lps[j] = i + 1;

i++;
j++;
}
else
{
if (i > 0)
i = lps[i - 1];
else
j++;
}

return lps;
}
}

Пояснение:
Данный код представляет собой класс `Solution`, в котором содержится метод `StrStr`, реализующий алгоритм поиска подстроки `needle` в строке `haystack` с использованием метода Knuth-Morris-Pratt (KMP). В методе используются вспомогательные методы `BuildLPS` для построения массива длин префиксов и суффиксов (LPS) для строки `needle`. Вот пояснение к данному методу:

1. Метод `StrStr` принимает две строки `haystack` и `needle` и возвращает индекс первого вхождения подстроки `needle` в строку `haystack`. Если подстрока не найдена, метод возвращает -1.
2. В методе создаются переменные `i` и `j`, которые используются для перемещения по строкам `haystack` и `needle` соответственно.
3. Сначала вызывается метод `BuildLPS` для строительства массива `lps` (LPS) для подстроки `needle`.
4. Затем запускается цикл `while`, который продолжается, пока не достигнут конец строки `haystack` или строки `needle`.
5. Внутри цикла проверяется, если текущие символы `haystack[i]` и `needle[j]` совпадают, то инкрементируются оба индекса для продолжения сравнения.
6. В случае несовпадения символов, в зависимости от значения `j` происходит смещение индекса `j` по массиву `lps`, либо увеличивается индекс `i`.
7. По завершению цикла проверяется, была ли найдена подстрока (`j == needle.Length`). Если подстрока найдена, то возвращается индекс начала найденной подстроки, иначе возвращается -1.

Метод `BuildLPS` строит массив LPS, который используется для эффективного сравнения строк при поиске подстроки. Он также использует алгоритм KMP для построения этого массива. В итоге код позволяет эффективно находить подстроки в строке, используя метод KMP.
👍3
Задача: 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.

Решение:
public class Solution {
public int Divide(int dividend, int divisor) {
if(divisor == 0 || dividend == int.MinValue && divisor == -1) return int.MaxValue;

int sign = (dividend < 0 ^ divisor < 0) ? -1 : 1, result = 0;

long m = Math.Abs((long)dividend), n = Math.Abs((long)divisor);

while(m >= n)
{
long subN = n;
for(int subCount = 1; m >= subN; subCount <<= 1, subN <<= 1)
{
m -= subN;
result += subCount;
}
}

return sign == 1 ? result : (result - result - result);
}
}

Пояснение:
Данный код представляет собой класс `Solution`, в котором содержится метод `Divide`, реализующий операцию деления без использования оператора деления. Этот метод выполняет деление `dividend` на `divisor` и возвращает результат деления. Вот пояснение к данному методу:

1. Метод `Divide` принимает два целых числа `dividend` и `divisor` и возвращает результат деления `dividend` на `divisor`.
2. В начале метода проверяется специальный случай: если `divisor` равен 0 или если `dividend` равен `int.MinValue` и `divisor` равен -1, то метод возвращает `int.MaxValue`. Это делается для предотвращения переполнения в случае деления на 0 или разделения `int.MinValue` на -1.
3. Затем определяется знак результата деления на основе знаков `dividend` и `divisor`. Если знаки разные, то результат будет отрицательным, иначе - положительным.
4. Затем переменные `m` и `n` инициализируются как модули от `dividend` и `divisor`, но уже `long`, чтобы избежать переполнения в процессе деления.
5. Запускается цикл `while`, который продолжается, пока `m` больше или равен `n`. Внутри цикла выполняется итеративное вычитание и увеличение результата на основе сдвига влево.
6. Наконец, возвращается результат деления, который корректируется в зависимости от знака в соответствии с вычисленным знаком. Если знак равен -1, результат умножается на -1.

Этот метод реализует алгоритм вычитания для нахождения результата деления без использования оператора деления, а также управляет специальными случаями, чтобы избежать переполнения.
👍1
Задача: 30. Substring with Concatenation of All Words #hard
Условие:
Вам дана строка s и массив строк-слов. Все строки слов имеют одинаковую длину.

Объединенная строка — это строка, которая в точности содержит все строки любой перестановки объединенных слов.

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

Решение:
public class Solution {
public IList<int> FindSubstring(string s, string[] words) {
var size = words[0].Length;
var window = size * words.Length;

var wordMap = new Dictionary<string, int[]>();
foreach (var word in words)
{
if (!wordMap.TryGetValue(word, out var map))
{
wordMap.Add(word, map = new int[2]);
}

map[0] += 1;
}

var checkMap = new List<(int index, string word)>();
for (var i = 0; i <= s.Length - size; i++)
{
var word = s[i..(i + size)];
if (wordMap.ContainsKey(word))
{
checkMap.Add((i, word));
}
}

var result = new List<int>();
var left = 0;
while (left < checkMap.Count - words.Length)
{
var checkWindow = checkMap[left].index + window - size;
var right = left;
var prevIndex = -size;
while (right < checkMap.Count && checkWindow >= checkMap[right].index)
{
if (checkMap[right].index >= prevIndex + size)
{
wordMap[checkMap[right].word][1] += 1;
prevIndex = checkMap[right].index;
}

right++;
}

var found = true;
foreach(var map in wordMap)
{
found &= map.Value[0] == map.Value[1];
map.Value[1] = 0;
}

if (found)
{
result.Add(checkMap[left].index);
}

left++;
}

return result;
}
}

Пояснение:
Данный код представляет собой класс `Solution`, в котором содержится метод `FindSubstring`, предназначенный для поиска подстрок в строке `s`, которые являются конкатенацией всех слов из массива `words`. Вот пояснение к данному методу:

1. В начале метода инициализируются переменные `size` и `window`. Переменная `size` содержит длину одного слова из массива `words`, а переменная `window` - общую длину всех слов из массива `words`.
2. Создается словарь `wordMap`, где ключом является слово из массива `words`, а значением - массив из двух элементов. Первый элемент массива используется для подсчета количества вхождений данного слова в массив `words` (значение `0` увеличивается на `1` при каждом добавлении слова).
3. Затем создается список `checkMap`, который содержит кортежи с индексом и самим словом из строки `s`, если это слово содержится в `wordMap`.
4. В цикле `while` осуществляется проверка каждого слова в `checkMap` и заполняется словарь `wordMap` вторыми значениями, показывающими количество соответствующих слов в текущем окне.
5. Затем происходит проверка, все ли слова из массива `words` содержатся в текущем окне. Если все слова содержатся, их вторые значения обнуляются и индекс начала окна добавляется в результат `result`.
6. Метод возвращает список индексов начала подстрок, где все слова массива `words` содержатся в строке `s` в нужной последовательности.

Данный метод реализует алгоритм поиска подстроки, составленной из всех слов массива, в заданной строке.
👍1
#medium
Задача: 31. Next Permutation

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

Например, для arr = [1,2,3] следующие являются всеми перестановками arr: [1,2,3], [1,3,2], [2, 1, 3], [2, 3, 1], [3,1,2], [3,2,1].
Следующая перестановка массива целых чисел — это следующая лексикографически большая перестановка его чисел. Более формально, если все перестановки массива отсортированы в одном контейнере по лексикографическому порядку, то следующая перестановка этого массива — это перестановка, следующая за ней в отсортированном контейнере. Если такое упорядочивание невозможно, массив должен быть переупорядочен в наименьший возможный порядок (то есть отсортирован по возрастанию).

Например, следующая перестановка arr = [1,2,3] — это [1,3,2].
Аналогично, следующая перестановка arr = [2,3,1] — это [3,1,2].
В то время как следующая перестановка arr = [3,2,1] — это [1,2,3], потому что [3,2,1] не имеет лексикографически большего переупорядочивания.

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

Замена должна быть выполнена на месте и использовать только постоянную дополнительную память.

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


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

1️⃣Мы меняем местами числа a[i−1] и a[j]. Теперь у нас есть правильное число на индексе i−1. Однако текущая перестановка ещё не является той перестановкой, которую мы ищем. Нам нужна наименьшая перестановка, которая может быть сформирована с использованием чисел только справа от a[i−1]. Следовательно, нам нужно расположить эти числа в порядке возрастания, чтобы получить их наименьшую перестановку.

2️⃣Однако, вспомните, что, сканируя числа справа налево, мы просто уменьшали индекс, пока не нашли пару a[i] и a[i−1], где a[i] > a[i−1]. Таким образом, все числа справа от a[i−1] уже были отсортированы в порядке убывания. Более того, обмен местами a[i−1] и a[j] не изменил этот порядок.

3️⃣Поэтому нам просто нужно перевернуть числа, следующие за a[i−1], чтобы получить следующую наименьшую лексикографическую перестановку.

😎 Решение:
public class Solution {
public void NextPermutation(int[] nums) {
int i = nums.Length - 2;
while (i >= 0 && nums[i + 1] <= nums[i]) {
i--;
}

if (i >= 0) {
int j = nums.Length - 1;
while (nums[j] <= nums[i]) {
j--;
}

Swap(nums, i, j);
}

Reverse(nums, i + 1);
}

private void Reverse(int[] nums, int start) {
int i = start, j = nums.Length - 1;
while (i < j) {
Swap(nums, i, j);
i++;
j--;
}
}

private void Swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}


🪙 466 вопроса вопроса на С# разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Пояснение:
Данный код реализует функцию NextPermutation для нахождения следующей перестановки массива nums в лексикографическом порядке. Вот краткое объяснение кода:

1. Создание массива и стэка: Создаются массив nextLessIndices, заполненный значениями -1, и стек nextLessStack для хранения индексов элементов, для которых найден следующий меньший элемент.

2. Поиск следующих меньших элементов: В цикле с обратным проходом по массиву nums ищутся следующие меньшие элементы для текущего элемента, обновляя nextLessIndices и nextLessStack.

3. Определение правого подмассива: Затем определяется правая часть массива, которая будет изменена: rightMost индексирует правые элементы, для которых существует следующий меньший элемент слева.

4. Обмен и сортировка: Если правая часть существует, значения элементов меняются местами, а затем правая часть массива сортируется, чтобы получить следующую лексикографическую перестановку. Если правая часть отсутствует, весь массив nums переворачивается.

Этот алгоритм эффективно обрабатывает массив, находя следующую лексикографическую перестановку на основе определения следующих меньших элементов и обмена значений.
#hard
Задача: 32. Longest Valid Parentheses

Дана строка, содержащая только символы '(' и ')'. Верните длину самой длинной подстроки с корректными (правильно сформированными) скобками.

Пример:
Input: s = "(()"
Output: 2


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

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

2️⃣Каждый раз, когда мы встречаем символ ‘(’, мы кладём его в стек. Для каждого встреченного символа ‘)’ мы извлекаем из стека символ ‘(’. Если символ ‘(’ недоступен в стеке для извлечения в любой момент или если в стеке остались элементы после обработки всей подстроки, подстрока скобок является некорректной.

3️⃣Таким образом, мы повторяем процесс для каждой возможной подстроки и продолжаем сохранять длину самой длинной найденной корректной строки.

😎 Решение:
public class Solution {
public boolean IsValid(String s) {
Stack<char> stack = new Stack<char>();
for (int i = 0; i < s.Length; i++) {
if (s[i] == '(') {
stack.Push('(');
} else if (stack.Count > 0 && stack.Peek() == '(') {
stack.Pop();
} else {
return false;
}
}
return stack.Count == 0;
}

public int LongestValidParentheses(String s) {
int maxlen = 0;
for (int i = 0; i < s.Length; i++) {
for (int j = i + 2; j <= s.Length; j += 2) {
if (IsValid(s.Substring(i, j - i))) {
maxlen = Math.Max(maxlen, j - i);
}
}
}
return maxlen;
}
}


🪙 466 вопроса вопроса на С# разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍1
#medium
Задача: 33. Search in Rotated Sorted Array

Есть массив целых чисел nums, отсортированный в порядке возрастания (с уникальными значениями).

Перед передачей в вашу функцию массив nums может быть повёрнут в неизвестном индексе поворота k (1 <= k < nums.length), так что результирующий массив будет иметь вид [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (с индексацией с нуля). Например, [0,1,2,4,5,6,7] может быть повёрнут в индексе поворота 3 и стать [4,5,6,7,0,1,2].

Для данного массива nums после возможного поворота и целого числа target, верните индекс target, если он есть в массиве, или -1, если его нет в массиве.

Вы должны написать алгоритм с временной сложностью O(log n).

Пример:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4


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

1️⃣Выполните двоичный поиск для определения индекса поворота, инициализируя границы области поиска значениями left = 0 и right = n - 1. Пока left < right:
Пусть mid = left + (right - left) // 2.
Если nums[mid] > nums[n - 1], это предполагает, что точка поворота находится справа от mid, следовательно, мы устанавливаем left = mid + 1. В противном случае, поворот может находиться на позиции mid или левее от mid, в этом случае мы должны установить right = mid.

2️⃣По завершении двоичного поиска мы имеем индекс поворота, обозначенный как pivot = left.
nums состоит из двух отсортированных подмассивов, nums[0 ~ left - 1] и nums[left ~ n - 1].

3️⃣Выполните двоичный поиск по подмассиву nums[0 ~ left - 1] для поиска target. Если target находится в этом подмассиве, верните его индекс.
В противном случае выполните двоичный поиск по подмассиву nums[left ~ n - 1] для поиска target. Если target находится в этом подмассиве, верните его индекс. В противном случае верните -1.

😎 Решение:
public class Solution {
public int Search(int[] nums, int target) {
int n = nums.Length;
int left = 0, right = n - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] > nums[n - 1]) {
left = mid + 1;
} else {
right = mid - 1;
}
}

int answer = BinarySearch(nums, 0, left - 1, target);
if (answer != -1) {
return answer;
}

return BinarySearch(nums, left, n - 1, target);
}

private int BinarySearch(int[] nums, int left_boundary, int right_boundary, int target) {
int left = left_boundary, right = right_boundary;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}

return -1;
}
}


🪙 466 вопроса вопроса на С# разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#medium
Задача: 34. Find First and Last Position of Element in Sorted Array

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

Если целевое значение не найдено в массиве, верните [-1, -1].

Вы должны написать алгоритм со временной сложностью O(log n).

Пример:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]


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

1️⃣Определите функцию под названием findBound, которая принимает три аргумента: массив, целевое значение для поиска и булевое значение isFirst, указывающее, ищем ли мы первое или последнее вхождение цели.
Мы используем 2 переменные для отслеживания подмассива, который мы сканируем. Назовем их begin и end. Изначально begin устанавливается в 0, а end — в последний индекс массива.

2️⃣Мы итерируем, пока begin не станет больше, чем end.
На каждом шаге мы вычисляем средний элемент mid = (begin + end) / 2. Мы используем значение среднего элемента, чтобы решить, какую половину массива нам нужно искать.
Если nums[mid] == target:
Если isFirst true — это означает, что мы пытаемся найти первое вхождение элемента. Если mid == begin или nums[mid - 1] != target, тогда мы возвращаем mid как первое вхождение цели. В противном случае мы обновляем end = mid - 1.
Если isFirst false — это означает, что мы пытаемся найти последнее вхождение элемента. Если mid == end или nums[mid + 1] != target, тогда мы возвращаем mid как последнее вхождение цели. В противном случае мы обновляем begin = mid + 1.

3️⃣Если nums[mid] > target — мы обновляем end = mid - 1, так как мы должны отбросить правую сторону массива, поскольку средний элемент больше цели.
Если nums[mid] < target — мы обновляем begin = mid + 1, так как мы должны отбросить левую сторону массива, поскольку средний элемент меньше цели.
В конце нашей функции мы возвращаем значение -1, что указывает на то, что цель не найдена в массиве.
В основной функции searchRange мы сначала вызываем findBound с isFirst, установленным в true. Если это значение равно -1, мы можем просто вернуть [-1, -1]. В противном случае мы вызываем findBound с isFirst, установленным в false, чтобы получить последнее вхождение, а затем возвращаем результат.

😎 Решение:
public class Solution {
public int[] SearchRange(int[] nums, int target) {
int firstOccurrence = this.FindBound(nums, target, true);
if (firstOccurrence == -1) {
return new int[] { -1, -1 };
}

int lastOccurrence = this.FindBound(nums, target, false);
return new int[] { firstOccurrence, lastOccurrence };
}

private int FindBound(int[] nums, int target, bool isFirst) {
int N = nums.Length;
int begin = 0, end = N - 1;
while (begin <= end) {
int mid = (begin + end) / 2;
if (nums[mid] == target) {
if (isFirst) {
if (mid == begin || nums[mid - 1] != target) {
return mid;
}

end = mid - 1;
} else {
if (mid == end || nums[mid + 1] != target) {
return mid;
}

begin = mid + 1;
}
} else if (nums[mid] > target) {
end = mid - 1;
} else {
begin = mid + 1;
}
}

return -1;
}
}


🪙 466 вопроса вопроса на С# разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#easy
Задача: 35. Search Insert Position

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

Вы должны написать алгоритм со временной сложностью O(log n).

Пример:
Input: nums = [1,3,5,6], target = 5
Output: 2


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

1️⃣Инициализируйте указатели left и right: left = 0, right = n - 1.

2️⃣Пока left <= right:
Сравните средний элемент массива nums[pivot] с целевым значением target.
Если средний элемент является целевым, то есть target == nums[pivot]: верните pivot.
Если цель не найдена:
Если target < nums[pivot], продолжайте поиск в левом подмассиве. right = pivot - 1.
Иначе продолжайте поиск в правом подмассиве. left = pivot + 1.
3️⃣Верните left.

😎 Решение:
public class Solution {
public int SearchInsert(int[] nums, int target) {
int pivot, left = 0, right = nums.Length - 1;
while (left <= right) {
pivot = left + (right - left) / 2;
if (nums[pivot] == target)
return pivot;
if (target < nums[pivot])
right = pivot - 1;
else
left = pivot + 1;
}

return left;
}
}


🪙 466 вопроса вопроса на С# разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#medium
Задача: 36. Valid Sudoku

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

Каждая строка должна содержать цифры от 1 до 9 без повторений.
Каждый столбец должен содержать цифры от 1 до 9 без повторений.
Каждая из девяти подзон размером 3 на 3 в сетке должна содержать цифры от 1 до 9 без повторений.

Пример:
Input: board = 
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: true


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

1️⃣Инициализируйте список из 9 хеш-множеств, где хеш-множество с индексом r будет использоваться для хранения ранее увиденных чисел в строке r судоку. Аналогично инициализируйте списки из 9 хеш-множеств для отслеживания столбцов и блоков.

2️⃣Итерируйтесь по каждой позиции (r, c) в судоку. На каждой итерации, если на текущей позиции есть число:
Проверьте, существует ли это число в хеш-множестве для текущей строки, столбца или блока. Если да, верните false, потому что это второе появление числа в текущей строке, столбце или блоке.

3️⃣В противном случае обновите множество, отвечающее за отслеживание ранее увиденных чисел в текущей строке, столбце и блоке. Индекс текущего блока рассчитывается как (r / 3) * 3 + (c / 3), где / означает деление нацело.
Если дубликаты не были найдены после посещения каждой позиции на доске судоку, то судоку валидно, поэтому верните true.

😎 Решение:
public class Solution {
public bool IsValidSudoku(char[][] board) {
int N = 9;
HashSet<char>[] rows = new HashSet<char>[N];
HashSet<char>[] cols = new HashSet<char>[N];
HashSet<char>[] boxes = new HashSet<char>[N];
for (int r = 0; r < N; r++) {
rows[r] = new HashSet<char>();
cols[r] = new HashSet<char>();
boxes[r] = new HashSet<char>();
}

for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
char val = board[r][c];
if (val == '.') {
continue;
}

if (rows[r].Contains(val)) {
return false;
}

rows[r].Add(val);
if (cols[c].Contains(val)) {
return false;
}

cols[c].Add(val);
int idx = (r / 3) * 3 + c / 3;
if (boxes[idx].Contains(val)) {
return false;
}

boxes[idx].Add(val);
}
}

return true;
}
}


🪙 466 вопроса вопроса на С# разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
#hard
Задача: 37. Sudoku Solver

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

Решение Судоку должно удовлетворять всем следующим правилам:

Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждой строке.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом столбце.
Каждая из цифр от 1 до 9 должна встречаться ровно один раз в каждом из 9 подблоков 3x3 сетки.
Символ '.' обозначает пустые ячейки.

Пример:
Input: board = 
[["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]]
Output:
[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]


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

1️⃣Теперь все готово для написания функции обратного поиска backtrack(row = 0, col = 0). Начните с верхней левой ячейки row = 0, col = 0. Продолжайте, пока не дойдете до первой свободной ячейки.

2️⃣Итерируйте по числам от 1 до 9 и попробуйте поставить каждое число d в ячейку (row, col).
Если число d еще не в текущей строке, столбце и блоке:
Поместите d в ячейку (row, col).
Запишите, что d теперь присутствует в текущей строке, столбце и блоке.

3️⃣Если вы на последней ячейке row == 8, col == 8:
Это означает, что судоку решено.
В противном случае продолжайте размещать дальнейшие числа.
Откат, если решение еще не найдено: удалите последнее число из ячейки (row, col).

😎 Решение:
using System;

public class Solution {
int n = 3;
int N;
int[][] rows, columns, boxes;
char[][] board;
bool sudokuSolved = false;

public Solution() {
N = n * n;
rows = new int[N][];
columns = new int[N][];
boxes = new int[N][];
for (int i = 0; i < N; i++) {
rows[i] = new int[N + 1];
columns[i] = new int[N + 1];
boxes[i] = new int[N + 1];
}
}

private bool CouldPlace(int d, int row, int col) {
int idx = (row / n) * n + col / n;
return rows[row][d] + columns[col][d] + boxes[idx][d] == 0;
}

private void PlaceOrRemove(int d, int row, int col, bool place) {
int idx = (row / n) * n + col / n;
int delta = place ? 1 : -1;
rows[row][d] += delta;
columns[col][d] += delta;
boxes[idx][d] += delta;
board[row][col] = place ? (char)(d + '0') : '.';
}

private void Backtrack(int row = 0, int col = 0) {
if (col == N) { col = 0; row++; }
if (row == N) { sudokuSolved = true; return; }

if (board[row][col] == '.') {
for (int d = 1; d <= 9; d++) {
if (CouldPlace(d, row, col)) {
PlaceOrRemove(d, row, col, true);
Backtrack(row, col + 1);
if (!sudokuSolved) PlaceOrRemove(d, row, col, false);
}
}
} else {
Backtrack(row, col + 1);
}
}

public void SolveSudoku(char[][] inputBoard) {
board = inputBoard;
for (int r = 0; r < N; r++) for (int c = 0; c < N; c++)
if (board[r][c] != '.') PlaceOrRemove((int)char.GetNumericValue(board[r][c]), r, c, true);

Backtrack();
}
}


🪙 466 вопроса вопроса на С# разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 38. Count and Say

Последовательность "считай и скажи" — это последовательность строк цифр, определяемая с помощью рекурсивной формулы:

countAndSay(1) = "1"
countAndSay(n) — это кодирование длин серий из countAndSay(n - 1).
Кодирование длин серий (RLE) — это метод сжатия строк, который работает путём замены последовательных идентичных символов (повторяющихся 2 или более раз) на конкатенацию символа и числа, обозначающего количество символов (длину серии). Например, чтобы сжать строку "3322251", мы заменяем "33" на "23", "222" на "32", "5" на "15" и "1" на "11". Таким образом, сжатая строка становится "23321511".

Для заданного положительного целого числа n верните n-й элемент последовательности "считай и скажи".

Пример:
Input: n = 4

Output: "1211"

Explanation:

countAndSay(1) = "1"
countAndSay(2) = RLE of "1" = "11"
countAndSay(3) = RLE of "11" = "21"
countAndSay(4) = RLE of "21" = "1211"


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

1️⃣Мы хотим использовать шаблон, который соответствует строкам из одинаковых символов, таких как "4", "7777", "2222222".
Если у вас есть опыт работы с регулярными выражениями, вы можете обнаружить, что шаблон (.)\1* работает.

2️⃣Мы можем разбить это регулярное выражение на три части:
(.): оно определяет группу, содержащую один символ, который может быть чем угодно.

3️⃣*: этот квалификатор, следующий за ссылкой на группу \1, указывает, что мы хотели бы видеть повторение группы ноль или более раз.
Таким образом, шаблон соответствует строкам, которые состоят из некоторого символа, а затем ноль или более повторений этого символа после его первого появления. Это то, что нам нужно.
Мы находим все совпадения с регулярным выражением, а затем конкатенируем результаты.

😎 Решение:
public class Solution {
public string CountAndSay(int n) {
string s = "1";
for (int i = 0; i < n - 1; i++) {
StringBuilder current = new StringBuilder();
for (int j = 0; j < s.Length; j++) {
int count = 1;
while (j < s.Length - 1 && s[j] == s[j + 1]) {
j++;
count++;
}

current.Append(count);
current.Append(s[j]);
}

s = current.ToString();
}

return s;
}
}


🪙 466 вопроса вопроса на С# разработчика

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
#medium
Задача: 39. Combination Sum

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

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

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

Пример:
Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]


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

1️⃣Как видно, вышеописанный алгоритм обратного отслеживания разворачивается как обход дерева в глубину (DFS - Depth-First Search), который часто реализуется с помощью рекурсии.
Здесь мы определяем рекурсивную функцию backtrack(remain, comb, start) (на Python), которая заполняет комбинации, начиная с текущей комбинации (comb), оставшейся суммы для выполнения (remain) и текущего курсора (start) в списке кандидатов.
Следует отметить, что сигнатура рекурсивной функции немного отличается в Java, но идея остается той же.

2️⃣Для первого базового случая рекурсивной функции, если remain == 0, то есть мы достигаем желаемой целевой суммы, поэтому мы можем добавить текущую комбинацию в итоговый список.
Как другой базовый случай, если remain < 0, то есть мы превышаем целевое значение, мы прекращаем исследование на этом этапе.

3️⃣Помимо вышеупомянутых двух базовых случаев, мы затем продолжаем исследовать подсписок кандидатов, начиная с [start ... n].
Для каждого из кандидатов мы вызываем рекурсивную функцию саму с обновленными параметрами.
Конкретно, мы добавляем текущего кандидата в комбинацию.
С добавленным кандидатом у нас теперь меньше суммы для выполнения, то есть remain - candidate.
Для следующего исследования мы все еще начинаем с текущего курсора start.
В конце каждого исследования мы делаем откат, удаляя кандидата из комбинации.

😎 Решение:
public class Solution {
public IList<IList<int>> CombinationSum(int[] candidates, int target) {
List<IList<int>> results = new List<IList<int>>();
this.backtrack(target, new List<int>(), candidates, 0, results);
return results;
}

private void backtrack(int remain, List<int> comb, int[] candidates,
int start, List<IList<int>> results) {
if (remain == 0) {
results.Add(new List<int>(comb));
return;
} else if (remain < 0) {
return;
}

for (int i = start; i < candidates.Length; ++i) {
comb.Add(candidates[i]);
this.backtrack(remain - candidates[i], comb, candidates, i,
results);
comb.RemoveAt(comb.Count - 1);
}
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#medium
Задача: 40. Combination Sum II

Дана коллекция кандидатов (candidates) и целевое число (target). Найдите все уникальные комбинации в candidates, где числа кандидатов в сумме дают target.

Каждое число в candidates может быть использовано только один раз в комбинации.

Примечание: Набор решений не должен содержать повторяющихся комбинаций.

Пример:
Input: candidates = [10,1,2,7,6,1,5], target = 8
Output:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]


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

1️⃣Во-первых, мы создаём таблицу счётчиков из предоставленного списка чисел. Затем мы используем эту таблицу счётчиков в процессе обратного поиска, который мы определяем как функцию backtrack(comb, remain, curr, candidate_groups, results). Для сохранения состояния на каждом этапе обратного поиска мы используем несколько параметров в функции:
comb: комбинация, которую мы построили на данный момент.
remain: оставшаяся сумма, которую нам нужно заполнить, чтобы достичь целевой суммы.
curr: курсор, который указывает на текущую группу чисел, используемую из таблицы счётчиков.
counter: текущая таблица счётчиков.
results: окончательные комбинации, которые достигают целевой суммы.

2️⃣При каждом вызове функции обратного поиска мы сначала проверяем, достигли ли мы целевой суммы (то есть sum(comb) = target), и нужно ли прекратить исследование, потому что сумма текущей комбинации превышает желаемую целевую сумму.

3️⃣Если осталась сумма для заполнения, мы затем перебираем текущую таблицу счётчиков, чтобы выбрать следующего кандидата. После выбора кандидата мы продолжаем исследование, вызывая функцию backtrack() с обновлёнными состояниями. Более важно, что в конце каждого исследования нам нужно вернуть состояние, которое мы обновили ранее, чтобы начать с чистого листа для следующего исследования. Именно из-за этой операции обратного поиска алгоритм получил своё название.

😎 Решение:
public class Solution {
List<IList<int>> Results = new List<IList<int>>();

public IList<IList<int>> CombinationSum2(int[] candidates, int target) {
Dictionary<int, int> counter = new Dictionary<int, int>();
foreach (int candidate in candidates) {
if (counter.ContainsKey(candidate))
counter[candidate] += 1;
else
counter[candidate] = 1;
}

List<int[]> counterList = new List<int[]>();
foreach (KeyValuePair<int, int> entry in counter) {
counterList.Add(new int[] { entry.Key, entry.Value });
}

Backtrack(new List<int>(), target, 0, counterList);
return Results;
}

private void Backtrack(List<int> comb, int remain, int curr,
List<int[]> counter) {
if (remain == 0) {
Results.Add(new List<int>(comb));
return;
}

if (remain < 0) {
return;
}

for (int nextCurr = curr; nextCurr < counter.Count; ++nextCurr) {
int[] entry = counter[nextCurr];
int candidate = entry[0], freq = entry[1];
if (freq <= 0)
continue;
comb.Add(candidate);
counter[nextCurr] = new int[] { candidate, freq - 1 };
Backtrack(comb, remain - candidate, nextCurr, counter);
counter[nextCurr] = new int[] { candidate, freq };
comb.RemoveAt(comb.Count - 1);
}
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
#hard
Задача: 41. First Missing Positive

Дан неотсортированный массив целых чисел nums. Верните наименьшее положительное целое число, которого нет в массиве nums.

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

Пример:
Input: nums = [3,4,-1,1]
Output: 2
Explanation: 1 is in the array but 2 is missing.


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

1️⃣Инициализировать переменную n длиной массива nums. Создать массив seen размером n + 1. Отметить элементы в массиве nums как просмотренные в массиве seen.
Для каждого числа num в массиве nums, если num больше 0 и меньше или равно n, установить seen[num] в значение true.

2️⃣Найти наименьшее недостающее положительное число:
Проитерировать от 1 до n, и если seen[i] не равно true, вернуть i как наименьшее недостающее положительное число.

3️⃣Если массив seen содержит все элементы от 1 до n, вернуть n + 1 как наименьшее недостающее положительное число.

😎 Решение:
public class Solution {
public int FirstMissingPositive(int[] nums) {
int n = nums.Length;
bool[] seen = new bool[n + 1];

foreach (int num in nums) {
if (num > 0 && num <= n) {
seen[num] = true;
}
}

for (int i = 1; i <= n; i++) {
if (!seen[i]) {
return i;
}
}

return n + 1;
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1