Задача: №16. 3Sum Closest #medium
Условие:
Решение:
Пояснение :
Проверка длины массива:
Сначала проверяется, содержит ли массив nums хотя бы три элемента. Если нет, метод возвращает 0, так как нет смысла искать сумму трёх элементов.
Сортировка массива:
Массив nums сортируется. Это необходимо для упрощения поиска комбинаций с использованием двух указателей.
Инициализация переменных:
start инициализируется на первом элементе, left на втором, и right на последнем элементе массива.
Переменные direction, minDistance, и sum инициализируются для отслеживания минимального расстояния до целевой суммы и самого близкого найденного значения суммы.
Внешний цикл:
Внешний цикл перебирает элементы массива начиная с первого. Для каждого элемента start он рассматривает комбинации элементов с использованием двух других указателей (left и right).
Внутренний цикл:
Внутренний цикл выполняется до тех пор, пока указатели left и right не пересекутся.
Рассчитывается сумма элементов, на которые указывают указатели start, left, и right.
Если сумма совпадает с целевой суммой, возвращается целевая сумма, так как это идеальный случай.
Обновление указателей и значений:
Если сумма меньше целевой, left сдвигается вправо (увеличивается), чтобы увеличить сумму.
Если сумма больше целевой, right сдвигается влево (уменьшается), чтобы уменьшить сумму.
При каждом изменении указателей обновляется минимальное расстояние между текущей суммой и целевой суммой, если новое расстояние меньше текущего.
Возвращение результата:
После завершения всех итераций возвращается значение sum, которое является суммой трех элементов, ближайшей к целевой сумме.
Временная и пространственная сложность:
Временная сложность: O(n^2), где n — количество элементов в массиве. Основная сложность обусловлена двойным перебором элементов массива (внешний цикл и внутренний цикл).
Пространственная сложность: O(1), так как используется фиксированное количество переменных для хранения данных и индексов, не зависящих от размера входных данных.
Условие:
Учитывая целочисленный массив nums длины n и целочисленную цель, найдите три целых числа в nums, сумма которых наиболее близка к цели. Возвращает сумму трех целых чисел. Вы можете предположить, что каждый вход будет иметь ровно одно решение.
Решение:
public class Solution {
public int ThreeSumClosest(int[] nums, int target) {
if(nums.Length < 3)
return 0;
Array.Sort(nums);
int start = 0;
int left = 1;
int right = nums.Length - 1;
int direction = nums[start] + nums[left] + nums[right] > 0 ? 1 : 0;
int minDistance = int.MaxValue;
int sum = int.MinValue;
while (start < nums.Length - 2)
{
while (left < right)
{
int currSum = nums[start] + nums[left] + nums[right];
if ( currSum == target )
return target;
if (currSum < target)
left++;
else
right--;
if (Math.Abs(currSum - target) < minDistance)
{
sum = currSum;
minDistance = Math.Abs(currSum - target);
}
}
start ++;
left = start + 1;
right = nums.Length - 1;
}
return sum;
}
}Пояснение :
Проверка длины массива:
Сначала проверяется, содержит ли массив nums хотя бы три элемента. Если нет, метод возвращает 0, так как нет смысла искать сумму трёх элементов.
Сортировка массива:
Массив nums сортируется. Это необходимо для упрощения поиска комбинаций с использованием двух указателей.
Инициализация переменных:
start инициализируется на первом элементе, left на втором, и right на последнем элементе массива.
Переменные direction, minDistance, и sum инициализируются для отслеживания минимального расстояния до целевой суммы и самого близкого найденного значения суммы.
Внешний цикл:
Внешний цикл перебирает элементы массива начиная с первого. Для каждого элемента start он рассматривает комбинации элементов с использованием двух других указателей (left и right).
Внутренний цикл:
Внутренний цикл выполняется до тех пор, пока указатели left и right не пересекутся.
Рассчитывается сумма элементов, на которые указывают указатели start, left, и right.
Если сумма совпадает с целевой суммой, возвращается целевая сумма, так как это идеальный случай.
Обновление указателей и значений:
Если сумма меньше целевой, left сдвигается вправо (увеличивается), чтобы увеличить сумму.
Если сумма больше целевой, right сдвигается влево (уменьшается), чтобы уменьшить сумму.
При каждом изменении указателей обновляется минимальное расстояние между текущей суммой и целевой суммой, если новое расстояние меньше текущего.
Возвращение результата:
После завершения всех итераций возвращается значение sum, которое является суммой трех элементов, ближайшей к целевой сумме.
Временная и пространственная сложность:
Временная сложность: O(n^2), где n — количество элементов в массиве. Основная сложность обусловлена двойным перебором элементов массива (внешний цикл и внутренний цикл).
Пространственная сложность: O(1), так как используется фиксированное количество переменных для хранения данных и индексов, не зависящих от размера входных данных.
👍4
Задача: №17. Letter Combinations of a Phone Number #medium
Условие:
Решение:
Пояснение:
Определение карты клавиатуры:
Словарь keypad содержит маппинг между цифрами и буквами, которые соответствуют этим цифрам на традиционной телефонной клавиатуре. Например, цифра '2' соответствует буквам 'a', 'b', 'c'.
Метод AddCombination:
Этот метод рекурсивно генерирует все возможные комбинации букв.
Принимает текущую комбинацию curr, строку цифр digits, текущий индекс index и список list, в который добавляются найденные комбинации.
Если индекс выходит за границы строки digits, это означает, что все цифры обработаны, и текущая комбинация добавляется в список list.
Если не все цифры обработаны, метод получает массив букв, соответствующих текущей цифре, из словаря keypad.
Для каждой буквы в массиве создаётся новая комбинация, и вызывается рекурсивный вызов метода для следующей цифры.
Метод LetterCombinations:
Этот метод инициализирует процесс генерации комбинаций, создавая список combinations для хранения результатов.
Если входная строка digits не пуста, вызывается метод AddCombination с начальными параметрами: пустой строкой для текущей комбинации, строкой цифр, начальным индексом 0 и списком результатов.
Возвращает список всех возможных комбинаций букв.
Временная и пространственная сложность:
Временная сложность: O(3^n), где n — количество цифр в строке digits. Это связано с тем, что для каждой цифры на клавиатуре есть набор соответствующих букв, и для каждой цифры мы можем иметь 3 или 4 комбинации, в зависимости от цифры.
Пространственная сложность: O(3^n), так как в худшем случае мы сохраняем все возможные комбинации в списке.
Условие:
Учитывая строку, содержащую цифры от 2 до 9 включительно, верните все возможные комбинации букв, которые может представлять число. Верните ответ в любом порядке. Соответствие цифр буквам (как на кнопках телефона) приведено ниже. Обратите внимание, что 1 не соответствует никаким буквам.
Решение:
public class Solution
{
Dictionary<char, char[]> keypad = new Dictionary<char, char[]> {{'2', new char[]{'a', 'b', 'c'}},
{'3', new char[]{'d', 'e', 'f'}}, {'4', new char[] {'g', 'h', 'i'}},
{'5', new char[] {'j', 'k', 'l'}}, {'6', new char[] {'m', 'n', 'o'}},
{'7', new char[] {'p', 'q', 'r', 's'}}, {'8', new char[] {'t', 'u', 'v'}},
{'9', new char[] {'w', 'x', 'y', 'z'}}};
public void AddCombination(string curr, string digits, int index, IList<string> list)
{
if(index >= digits.Length) list.Add(curr);
else
{
char[] map = keypad[digits[index]];
for(int i = 0; i < map.Length; i++)
{
string newCurr = curr + map[i];
AddCombination(newCurr, digits, index + 1, list);
}
}
}
public IList<string> LetterCombinations(string digits)
{
IList<string> combinations = new List<string>();
if(digits.Length > 0) AddCombination("", digits, 0, combinations);
return combinations;
}
}
Пояснение:
Определение карты клавиатуры:
Словарь keypad содержит маппинг между цифрами и буквами, которые соответствуют этим цифрам на традиционной телефонной клавиатуре. Например, цифра '2' соответствует буквам 'a', 'b', 'c'.
Метод AddCombination:
Этот метод рекурсивно генерирует все возможные комбинации букв.
Принимает текущую комбинацию curr, строку цифр digits, текущий индекс index и список list, в который добавляются найденные комбинации.
Если индекс выходит за границы строки digits, это означает, что все цифры обработаны, и текущая комбинация добавляется в список list.
Если не все цифры обработаны, метод получает массив букв, соответствующих текущей цифре, из словаря keypad.
Для каждой буквы в массиве создаётся новая комбинация, и вызывается рекурсивный вызов метода для следующей цифры.
Метод LetterCombinations:
Этот метод инициализирует процесс генерации комбинаций, создавая список combinations для хранения результатов.
Если входная строка digits не пуста, вызывается метод AddCombination с начальными параметрами: пустой строкой для текущей комбинации, строкой цифр, начальным индексом 0 и списком результатов.
Возвращает список всех возможных комбинаций букв.
Временная и пространственная сложность:
Временная сложность: O(3^n), где n — количество цифр в строке digits. Это связано с тем, что для каждой цифры на клавиатуре есть набор соответствующих букв, и для каждой цифры мы можем иметь 3 или 4 комбинации, в зависимости от цифры.
Пространственная сложность: O(3^n), так как в худшем случае мы сохраняем все возможные комбинации в списке.
👍1
Задача: №18. 4Sum #medium
Условие:
Решение:
Пояснение:
Метод FourSum:
Этот метод инициализирует основной процесс поиска, создавая список rs для хранения результатов.
Массив nums сортируется для упрощения дальнейшей логики.
Затем вызывается вспомогательный метод Helper с параметрами: результатами, отсортированным массивом, целевым значением, количеством элементов для поиска (в данном случае 4), начальным индексом и пустым списком для временного хранения текущего поднабора.
Метод Helper:
Этот метод рекурсивно находит наборы чисел, которые соответствуют заданному количеству k и суммируются до указанного целевого значения target.
База рекурсии (k == 2): Когда требуется найти пары чисел, функция использует двухуказательный подход. l начинает с начального индекса, r — с конца массива. В цикле проверяется сумма чисел на этих позициях:
Если сумма равна target, текущая пара добавляется в временный список subList, и результат добавляется в основной список rs. После добавления результаты очищаются для следующего возможного набора.
Если сумма меньше target, увеличивается индекс l, чтобы попробовать большее число.
Если сумма больше target, уменьшается индекс r, чтобы попробовать меньшее число.
Рекурсивный вызов (k > 2): При большем количестве элементов для поиска (например, 4) метод перебирает элементы массива начиная с указанного индекса. Для каждого элемента:
Если текущий элемент такой же, как предыдущий, пропускается, чтобы избежать дублирования.
Текущий элемент добавляется в subList, и вызывается рекурсивно с уменьшением k на 1 и обновлением целевого значения (target - nums[i]).
После возвращения из рекурсии элемент удаляется из subList для возможности обработки следующих комбинаций.
Временная и пространственная сложность:
Временная сложность: О(n^3), так как в самом худшем случае каждая из рекурсий может быть вызвана до трех уровней вложенности, где n — длина массива nums.
Пространственная сложность: O(n), включая список для хранения подмножеств и рекурсивный стек.
Условие:
Учитывая массив nums из n целых чисел, верните массив всех уникальных четверок [nums[a], nums[b], nums[c], nums[d]] таких, что:
0 <= a, b, c, d < n
a, b, c и d различны.
nums[a] + nums[b] + nums[c] + nums[d] == цель
Вы можете вернуть ответ в любом порядке.
Решение:
public class Solution {
public IList<IList<int>> FourSum(int[] nums, int target) {
var rs = new List<IList<int>>();
Array.Sort(nums);
Helper(rs, nums, target, 4, 0, new List<int>());
return rs;
}
private void Helper(List<IList<int>> rs, int[] nums, long target, int k, int index, List<int> subList) {
if (k == 2) {
int l = index;
int r = nums.Length - 1;
while (l < r) {
long sum = nums[l] + nums[r];
if (sum == target) {
subList.Add(nums[l]);
subList.Add(nums[r]);
rs.Add(new List<int>(subList));
subList.RemoveAt(subList.Count - 1);
subList.RemoveAt(subList.Count - 1);
while (l < r && nums[l] == nums[l + 1]) {
l++;
}
while (l < r && nums[r - 1] == nums[r]) {
r--;
}
l++;
r--;
} else if (sum < target) {
l++;
} else {
r--;
}
}
} else {
for (int i = index; i < nums.Length - k + 1; i++) {
if (i != index && nums[i] == nums[i - 1]) {
continue;
}
subList.Add(nums[i]);
Helper(rs, nums, target - nums[i], k - 1, i + 1, subList);
subList.RemoveAt(subList.Count - 1);
}
}
}
}Пояснение:
Метод FourSum:
Этот метод инициализирует основной процесс поиска, создавая список rs для хранения результатов.
Массив nums сортируется для упрощения дальнейшей логики.
Затем вызывается вспомогательный метод Helper с параметрами: результатами, отсортированным массивом, целевым значением, количеством элементов для поиска (в данном случае 4), начальным индексом и пустым списком для временного хранения текущего поднабора.
Метод Helper:
Этот метод рекурсивно находит наборы чисел, которые соответствуют заданному количеству k и суммируются до указанного целевого значения target.
База рекурсии (k == 2): Когда требуется найти пары чисел, функция использует двухуказательный подход. l начинает с начального индекса, r — с конца массива. В цикле проверяется сумма чисел на этих позициях:
Если сумма равна target, текущая пара добавляется в временный список subList, и результат добавляется в основной список rs. После добавления результаты очищаются для следующего возможного набора.
Если сумма меньше target, увеличивается индекс l, чтобы попробовать большее число.
Если сумма больше target, уменьшается индекс r, чтобы попробовать меньшее число.
Рекурсивный вызов (k > 2): При большем количестве элементов для поиска (например, 4) метод перебирает элементы массива начиная с указанного индекса. Для каждого элемента:
Если текущий элемент такой же, как предыдущий, пропускается, чтобы избежать дублирования.
Текущий элемент добавляется в subList, и вызывается рекурсивно с уменьшением k на 1 и обновлением целевого значения (target - nums[i]).
После возвращения из рекурсии элемент удаляется из subList для возможности обработки следующих комбинаций.
Временная и пространственная сложность:
Временная сложность: О(n^3), так как в самом худшем случае каждая из рекурсий может быть вызвана до трех уровней вложенности, где n — длина массива nums.
Пространственная сложность: O(n), включая список для хранения подмножеств и рекурсивный стек.
👍1
Задача: №19. Remove Nth Node From End of List #medium
Условие:
Решение:
Пояснение:
Инициализация переменных:
i — счетчик для отслеживания количества узлов, начиная с начала списка.
cur — указатель, начинающийся с головы списка, используется для прохода по списку.
prev — указатель, инициализированный как null. Он используется для отслеживания предыдущего узла, когда cur проходит по списку.
Проход по списку:
Цикл while продолжается до тех пор, пока cur не станет null (то есть пока не достигнут конец списка).
В теле цикла сначала проверяется, достиг ли счетчик i значения n. Если да, prev будет указывать на узел, который находится n узлов перед текущим узлом, или на саму голову списка, если i меньше n.
Затем i увеличивается, а cur перемещается к следующему узлу.
Удаление узла:
Если prev остается null, это означает, что нужно удалить первый узел (голову списка). Возвращается следующий узел после головы (head.next).
Если prev не null и его следующая ссылка (prev.next) существует, эта ссылка обновляется, чтобы пропустить следующий узел (prev.next = prev.next.next).
Возвращение нового списка:
Если узел был успешно удален, функция возвращает голову списка. В случае удаления первого узла, возвращается новый начальный узел, иначе возвращается исходный список.
Временная и пространственная сложность:
Временная сложность: O(L), где L — длина списка. Функция проходит по всем узлам списка один раз.
Пространственная сложность: O(1). Используется фиксированное количество дополнительных переменных, независимо от размера входного списка.
Условие:
Учитывая заголовок связанного списка, удалите n-й узел из конца списка и верните его заголовок.
Решение:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/
public class Solution {
public ListNode RemoveNthFromEnd(ListNode head, int n) {
int length = 0;
ListNode curr = head;
// Find the length of the linked list
while (curr != null) {
length++;
curr = curr.next;
}
int traverseTill = length - n - 1;
curr = head;
// Traverse to the node before the one to be removed
for (int i = 0; i < traverseTill; i++) {
curr = curr.next;
}
// Remove the nth node from the end
if (traverseTill == -1) {
return head.next;
} else {
curr.next = curr.next.next;
return head;
}
}
}
Пояснение:
Инициализация переменных:
i — счетчик для отслеживания количества узлов, начиная с начала списка.
cur — указатель, начинающийся с головы списка, используется для прохода по списку.
prev — указатель, инициализированный как null. Он используется для отслеживания предыдущего узла, когда cur проходит по списку.
Проход по списку:
Цикл while продолжается до тех пор, пока cur не станет null (то есть пока не достигнут конец списка).
В теле цикла сначала проверяется, достиг ли счетчик i значения n. Если да, prev будет указывать на узел, который находится n узлов перед текущим узлом, или на саму голову списка, если i меньше n.
Затем i увеличивается, а cur перемещается к следующему узлу.
Удаление узла:
Если prev остается null, это означает, что нужно удалить первый узел (голову списка). Возвращается следующий узел после головы (head.next).
Если prev не null и его следующая ссылка (prev.next) существует, эта ссылка обновляется, чтобы пропустить следующий узел (prev.next = prev.next.next).
Возвращение нового списка:
Если узел был успешно удален, функция возвращает голову списка. В случае удаления первого узла, возвращается новый начальный узел, иначе возвращается исходный список.
Временная и пространственная сложность:
Временная сложность: O(L), где L — длина списка. Функция проходит по всем узлам списка один раз.
Пространственная сложность: O(1). Используется фиксированное количество дополнительных переменных, независимо от размера входного списка.
👍2
Задача: №21. Merge Two Sorted Lists #easy
Условие:
Решение:
Пояснение:
Создание фиктивного узла:
dummyHead — это фиктивный узел, который используется для упрощения операции слияния. Он помогает избежать необходимости в отдельных проверках на начало списка и управления указателем на голову нового списка.
Текущий узел:
current — указатель, который используется для построения нового связанного списка. Он начинается с фиктивного узла и будет перемещаться вперед по мере добавления узлов в результирующий список.
Цикл слияния:
Цикл продолжается до тех пор, пока не закончится один из списков (list1 или list2). Внутри цикла:
Сравниваются значения текущих узлов обоих списков.
Узел с меньшим значением добавляется к результирующему списку.
Указатель на текущий элемент добавляется в результат, а соответствующий указатель (list1 или list2) передвигается к следующему элементу.
Добавление оставшихся узлов:
После завершения цикла один из списков может содержать оставшиеся элементы. Цикл завершается, и все оставшиеся узлы второго списка добавляются к результату. Это делается просто путем присоединения оставшихся узлов одного списка к результирующему списку.
Возврат результата:
Возвращается новый связанный список, начиная с узла, следующего за фиктивным узлом (dummyHead.next). Фиктивный узел был добавлен только для упрощения процесса добавления элементов, и он не является частью фактического итогового списка.
Временная и пространственная сложность:
Временная сложность: O(n + m), где n и m — длины входных списков list1 и list2 соответственно. Всякий раз, когда элементы сравниваются и добавляются, это происходит один раз для каждого элемента из обоих списков.
Пространственная сложность: O(1). Дополнительная память используется только для временных переменных и фиктивного узла. Все элементы входных списков используются непосредственно в результирующем списке, и не требуется дополнительное выделение памяти для хранения их значений.
Условие:
Вам даны заголовки двух отсортированных связанных списков list1 и list2. Объедините два списка в один отсортированный список. Список должен быть составлен путем сращивания узлов первых двух списков. Возвращает заголовок объединенного связанного списка.
Решение:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/
public class Solution {
public ListNode MergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummyHead = new ListNode(-1);
// Create a dummy node as the starting point
ListNode current = dummyHead;
// Pointer to keep track of the current node
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
current.next = list1;
list1 = list1.next;
} else {
current.next = list2;
list2 = list2.next;
}
current = current.next;
}
// Attach the remaining nodes if any
if (list1 != null) {
current.next = list1;
} else {
current.next = list2;
}
return dummyHead.next;
// Return the merged list starting from the node after the dummy head
}
}
Пояснение:
Создание фиктивного узла:
dummyHead — это фиктивный узел, который используется для упрощения операции слияния. Он помогает избежать необходимости в отдельных проверках на начало списка и управления указателем на голову нового списка.
Текущий узел:
current — указатель, который используется для построения нового связанного списка. Он начинается с фиктивного узла и будет перемещаться вперед по мере добавления узлов в результирующий список.
Цикл слияния:
Цикл продолжается до тех пор, пока не закончится один из списков (list1 или list2). Внутри цикла:
Сравниваются значения текущих узлов обоих списков.
Узел с меньшим значением добавляется к результирующему списку.
Указатель на текущий элемент добавляется в результат, а соответствующий указатель (list1 или list2) передвигается к следующему элементу.
Добавление оставшихся узлов:
После завершения цикла один из списков может содержать оставшиеся элементы. Цикл завершается, и все оставшиеся узлы второго списка добавляются к результату. Это делается просто путем присоединения оставшихся узлов одного списка к результирующему списку.
Возврат результата:
Возвращается новый связанный список, начиная с узла, следующего за фиктивным узлом (dummyHead.next). Фиктивный узел был добавлен только для упрощения процесса добавления элементов, и он не является частью фактического итогового списка.
Временная и пространственная сложность:
Временная сложность: O(n + m), где n и m — длины входных списков list1 и list2 соответственно. Всякий раз, когда элементы сравниваются и добавляются, это происходит один раз для каждого элемента из обоих списков.
Пространственная сложность: O(1). Дополнительная память используется только для временных переменных и фиктивного узла. Все элементы входных списков используются непосредственно в результирующем списке, и не требуется дополнительное выделение памяти для хранения их значений.
👍1
Задача: 22. Generate Parentheses #medium
Условие:
Решение:
Пояснение:
1. Класс `Solution` содержит приватное поле `result`, представленное интерфейсом `IList<string>`, инициализированное пустым списком типа `List<string>`. Это поле предназначено для хранения сгенерированных последовательностей скобок.
2. Публичный метод `GenerateParenthesis` принимает целочисленный аргумент `n`, который представляет количество пар скобок, которые нужно сгенерировать. Внутри этого метода вызывается приватный метод `Helper` для генерации скобочных последовательностей.
3. Приватный метод `Helper` принимает `StringBuilder cur` для построения текущей последовательности, а также два целочисленных параметра `l` и `r`, представляющих количество оставшихся открывающих и закрывающих скобок соответственно. Метод `Helper` рекурсивно создает допустимые комбинации скобок с помощью условий и операций добавления и удаления скобок из `cur`.
4. Базовый случай в методе `Helper` проверяет, достигло ли количество закрывающих скобок `r` нуля. В этом случае текущая последовательность скобок добавляется в список `result`.
5. В зависимости от количества оставшихся открывающих и закрывающих скобок, метод `Helper` решает, можно ли добавить открывающую или закрывающую скобку и вызывает рекурсивно себя для генерации остальных комбинаций.
6. В конце метода `GenerateParenthesis` возвращается список `result`, содержащий все допустимые комбинации скобок для заданного количества пар скобок `n`. Каждая комбинация представлена строкой без явного использования символов.
Условие:
Учитывая n пар круглых скобок, напишите функцию, которая генерирует все комбинации правильных круглых скобок.
Решение:
public class Solution {
private IList<string> result = new List<string>();
public IList<string> GenerateParenthesis(int n)
{
Helper(new StringBuilder(), n, n);
return result;
}
private void Helper(StringBuilder cur, int l, int r)
{
if (r == 0)
result.Add(cur.ToString());
else
{
if (l == r)
{
cur.Append('(');
Helper(cur, l - 1, r);
cur.Remove(cur.Length - 1, 1);
}
else
{
if (l > 0)
{
cur.Append('(');
Helper(cur, l - 1, r);
cur.Remove(cur.Length - 1, 1);
}
if (r > 0)
{
cur.Append(')');
Helper(cur, l, r - 1);
cur.Remove(cur.Length - 1, 1);
}
}
}
}
}Пояснение:
1. Класс `Solution` содержит приватное поле `result`, представленное интерфейсом `IList<string>`, инициализированное пустым списком типа `List<string>`. Это поле предназначено для хранения сгенерированных последовательностей скобок.
2. Публичный метод `GenerateParenthesis` принимает целочисленный аргумент `n`, который представляет количество пар скобок, которые нужно сгенерировать. Внутри этого метода вызывается приватный метод `Helper` для генерации скобочных последовательностей.
3. Приватный метод `Helper` принимает `StringBuilder cur` для построения текущей последовательности, а также два целочисленных параметра `l` и `r`, представляющих количество оставшихся открывающих и закрывающих скобок соответственно. Метод `Helper` рекурсивно создает допустимые комбинации скобок с помощью условий и операций добавления и удаления скобок из `cur`.
4. Базовый случай в методе `Helper` проверяет, достигло ли количество закрывающих скобок `r` нуля. В этом случае текущая последовательность скобок добавляется в список `result`.
5. В зависимости от количества оставшихся открывающих и закрывающих скобок, метод `Helper` решает, можно ли добавить открывающую или закрывающую скобку и вызывает рекурсивно себя для генерации остальных комбинаций.
6. В конце метода `GenerateParenthesis` возвращается список `result`, содержащий все допустимые комбинации скобок для заданного количества пар скобок `n`. Каждая комбинация представлена строкой без явного использования символов.
👍2
Задача: 23. Merge k Sorted Lists #hard
Условие:
Решение:
Пояснение:
осле окончания цикла, оставшиеся элементы списка добавляются в конец нового списка.
В итоге, данный код реализует эффективный способ объединения нескольких отсортированных списков в один общий отсортированный список, используя подход "разделяй и властвуй" и пространственную сложность O(1).
Условие:
Вам дан массив из 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
Условие:
Решение:
Пояснение:
Данный код реализует функцию 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 попарно обменивает значения узлов в односвязном списке до конца списка, сохраняя ссылку на начало списка.
Условие:
Учитывая связанный список, поменяйте местами каждые два соседних узла и верните его голову. Вы должны решить проблему, не изменяя значения в узлах списка (т. е. изменять можно только сами узлы).
Решение:
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
Условие:
Решение:
Пояснение:
1. ReverseList: Это приватный метод, который реализует обращение части связанного списка между позициями m и n. Он обновляет указатель головы группы groupHead, пройдя по списку и меняя указатели в нужной последовательности.
2. ReverseKGroup: Это публичный метод, который реверсирует группы узлов по k элементов в связанном списке. Он сначала определяет общее количество узлов в списке, затем поочередно вызывает ReverseList для групп узлов длиной k. После завершения обработки всех групп он возвращает новую голову списка.
Общий подход заключается в том, что ReverseList изменяет указатели узлов для обращения части списка, а ReverseKGroup последовательно применяет ReverseList для групп узлов длиной k и обновляет указатели в соответствии с этим. Код решает задачу обращения групп узлов в связанном списке.
Условие:
Учитывая заголовок связанного списка, поменяйте местами узлы списка 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
Условие:
Решение:
Пояснение:
Данный код представляет собой класс `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` после завершения всех итераций, представляющее количество уникальных элементов, которые остались в массиве после удаления повторяющихся. Таким образом, код эффективно удаляет дубликаты из упорядоченного массива, сохраняя порядок оставшихся уникальных элементов.
Условие:
Учитывая целочисленный массив чисел, отсортированный в неубывающем порядке, удалите дубликаты на месте так, чтобы каждый уникальный элемент появлялся только один раз. Относительный порядок элементов должен оставаться неизменным. Затем верните количество уникальных элементов в числах.
Считайте, что количество уникальных элементов чисел равно 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