Задача: 959. Regions Cut By Slashes
Сложность: medium
n x n сетка состоит из квадратов размером 1 x 1, где каждый квадрат 1 x 1 содержит '/', '', или пустое пространство ' '. Эти символы делят квадрат на смежные области.
Дана сетка grid, представленная в виде строкового массива. Верните количество областей.
Обратите внимание, что обратные слеши экранированы, поэтому '' представлен как '\'.
Пример:
👨💻 Алгоритм:
1⃣Создайте 4*N*N узлов для каждой ячейки сетки и соедините их в соответствии с описанием.
2⃣Используйте структуру объединения-поиска (DSU), чтобы найти количество связанных компонентов.
3⃣Пройдите по всем узлам и посчитайте количество корневых узлов, которые представляют количество областей.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
n x n сетка состоит из квадратов размером 1 x 1, где каждый квадрат 1 x 1 содержит '/', '', или пустое пространство ' '. Эти символы делят квадрат на смежные области.
Дана сетка grid, представленная в виде строкового массива. Верните количество областей.
Обратите внимание, что обратные слеши экранированы, поэтому '' представлен как '\'.
Пример:
Input: grid = [" /","/ "]
Output: 2
👨💻 Алгоритм:
1⃣Создайте 4*N*N узлов для каждой ячейки сетки и соедините их в соответствии с описанием.
2⃣Используйте структуру объединения-поиска (DSU), чтобы найти количество связанных компонентов.
3⃣Пройдите по всем узлам и посчитайте количество корневых узлов, которые представляют количество областей.
😎 Решение:
public class DSU {
int[] parent;
public DSU(int N) {
parent = new int[N];
for (int i = 0; i < N; ++i)
parent[i] = i;
}
public int Find(int x) {
if (parent[x] != x) parent[x] = Find(parent[x]);
return parent[x];
}
public void Union(int x, int y) {
parent[Find(x)] = Find(y);
}
}
public class Solution {
public int RegionsBySlashes(string[] grid) {
int N = grid.Length;
DSU dsu = new DSU(4 * N * N);
for (int r = 0; r < N; ++r) {
for (int c = 0; c < N; ++c) {
int root = 4 * (r * N + c);
char val = grid[r][c];
if (val != '\\') {
dsu.Union(root + 0, root + 1);
dsu.Union(root + 2, root + 3);
}
if (val != '/') {
dsu.Union(root + 0, root + 2);
dsu.Union(root + 1, root + 3);
}
if (r + 1 < N) {
dsu.Union(root + 3, (root + 4 * N) + 0);
}
if (r - 1 >= 0) {
dsu.Union(root + 0, (root - 4 * N) + 3);
}
if (c + 1 < N) {
dsu.Union(root + 2, (root + 4) + 1);
}
if (c - 1 >= 0) {
dsu.Union(root + 1, (root - 4) + 2);
}
}
}
int ans = 0;
for (int x = 0; x < 4 * N * N; ++x) {
if (dsu.Find(x) == x) {
++ans;
}
}
return ans;
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 146. LRU Cache
Сложность: medium
Реализуйте класс LRUCache:
LRUCache(int capacity) - инициализирует LRU-кэш с положительным размером capacity.
int get(int key) - возвращает значение по ключу, если ключ существует, в противном случае возвращает -1.
void put(int key, int value) - обновляет значение по ключу, если ключ существует. В противном случае добавляет пару ключ-значение в кэш. Если количество ключей превышает установленную емкость после этой операции, удаляет наименее недавно использованный ключ.
Функции get и put должны выполняться за среднее время O(1).
Пример:
👨💻 Алгоритм:
1⃣Метод добавления узла в конец связного списка (add):
Получите текущий узел в конце списка, это "реальный" хвост: tail.prev, обозначим его как previousEnd.
Вставьте node после previousEnd, установив previousEnd.next = node.
Настройте указатели узла: node.prev = previousEnd и node.next = tail.
Обновите tail.prev = node, делая node новым "реальным" хвостом списка.
2⃣Метод удаления узла из связного списка (remove):
Узел node должен быть удален из списка. Для этого определите узлы nextNode = node.next и prevNode = node.prev.
Чтобы удалить node, переназначьте prevNode.next = nextNode и nextNode.prev = prevNode, эффективно исключая node из списка.
Это превратит, например, последовательность A <-> B <-> C в A <-> C, где prevNode = A и nextNode = C.
3⃣Методы get и put:
get(int key): Проверьте, существует ли ключ в хэш-карте. Если нет, верните -1. Иначе, получите узел, связанный с ключом, переместите его в конец списка с помощью remove(node) и add(node). Верните node.val.
put(int key, int value): Если ключ уже существует, найдите соответствующий узел и удалите его методом remove. Создайте новый узел с key и value, добавьте его в хэш-карту и в конец списка методом add(node). Если размер кэша превышает установленную емкость после добавления, удалите самый редко используемый узел (который находится в голове списка после фиктивного узла head), затем удалите соответствующий ключ из хэш-карты.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Реализуйте класс LRUCache:
LRUCache(int capacity) - инициализирует LRU-кэш с положительным размером capacity.
int get(int key) - возвращает значение по ключу, если ключ существует, в противном случае возвращает -1.
void put(int key, int value) - обновляет значение по ключу, если ключ существует. В противном случае добавляет пару ключ-значение в кэш. Если количество ключей превышает установленную емкость после этой операции, удаляет наименее недавно использованный ключ.
Функции get и put должны выполняться за среднее время O(1).
Пример:
Input
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
Output
[null, null, null, 1, null, -1, null, -1, 3, 4]
👨💻 Алгоритм:
1⃣Метод добавления узла в конец связного списка (add):
Получите текущий узел в конце списка, это "реальный" хвост: tail.prev, обозначим его как previousEnd.
Вставьте node после previousEnd, установив previousEnd.next = node.
Настройте указатели узла: node.prev = previousEnd и node.next = tail.
Обновите tail.prev = node, делая node новым "реальным" хвостом списка.
2⃣Метод удаления узла из связного списка (remove):
Узел node должен быть удален из списка. Для этого определите узлы nextNode = node.next и prevNode = node.prev.
Чтобы удалить node, переназначьте prevNode.next = nextNode и nextNode.prev = prevNode, эффективно исключая node из списка.
Это превратит, например, последовательность A <-> B <-> C в A <-> C, где prevNode = A и nextNode = C.
3⃣Методы get и put:
get(int key): Проверьте, существует ли ключ в хэш-карте. Если нет, верните -1. Иначе, получите узел, связанный с ключом, переместите его в конец списка с помощью remove(node) и add(node). Верните node.val.
put(int key, int value): Если ключ уже существует, найдите соответствующий узел и удалите его методом remove. Создайте новый узел с key и value, добавьте его в хэш-карту и в конец списка методом add(node). Если размер кэша превышает установленную емкость после добавления, удалите самый редко используемый узел (который находится в голове списка после фиктивного узла head), затем удалите соответствующий ключ из хэш-карты.
😎 Решение:
public class Node {
public int key { get; set; }
public int value { get; set; }
public Node next { get; set; }
public Node prev { get; set; }
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
public class LRUCache {
private int capacity;
private Dictionary<int, Node> dic;
private Node head;
private Node tail;
public LRUCache(int capacity) {
this.capacity = capacity;
dic = new Dictionary<int, Node>();
head = new Node(-1, -1);
tail = new Node(-1, -1);
head.next = tail;
tail.prev = head;
}
public int Get(int key) {
if (!dic.ContainsKey(key)) {
return -1;
}
Node node = dic[key];
Remove(node);
Add(node);
return node.value;
}
public void Put(int key, int value) {
if (dic.ContainsKey(key)) {
Node oldNode = dic[key];
Remove(oldNode);
}
Node node = new Node(key, value);
dic[key] = node;
Add(node);
if (dic.Count > capacity) {
Node nodeToDelete = head.next;
Remove(nodeToDelete);
dic.Remove(nodeToDelete.key);
}
}
private void Add(Node node) {
Node previousEnd = tail.prev;
previousEnd.next = node;
node.prev = previousEnd;
node.next = tail;
tail.prev = node;
}
private void Remove(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 1472. Design Browser History
Сложность: medium
У вас есть браузер с одной вкладкой, где вы начинаете на домашней странице и можете посетить другой URL, вернуться назад на определённое количество шагов в истории или переместиться вперёд на определённое количество шагов в истории.
Реализуйте класс BrowserHistory:
BrowserHistory(string homepage) Инициализирует объект с домашней страницей браузера.
void visit(string url) Посещает URL с текущей страницы. Это очищает всю историю вперёд.
string back(int steps) Перемещает на steps шагов назад в истории. Если вы можете вернуться только на x шагов в истории, а steps > x, вы вернётесь только на x шагов. Возвращает текущий URL после перемещения назад в истории на не более чем steps шагов.
string forward(int steps) Перемещает на steps шагов вперёд в истории. Если вы можете переместиться только на x шагов вперёд в истории, а steps > x, вы переместитесь только на x шагов. Возвращает текущий URL после перемещения вперёд в истории на не более чем steps шагов.
Пример:
👨💻 Алгоритм:
1⃣Инициализация:
Создайте класс BrowserHistory с двумя стеками (history и future) и строковой переменной current для хранения текущего URL. Инициализируйте current с домашней страницей.
2⃣Посещение URL:
Метод visit(url) сохраняет текущий URL в стеке history, устанавливает url как текущий и очищает стек future.
3⃣Навигация назад и вперед:
Метод back(steps) перемещает текущий URL в стек future и извлекает URL из стека history, пока шаги не будут исчерпаны или стек history не станет пустым.
Метод forward(steps) перемещает текущий URL в стек history и извлекает URL из стека future, пока шаги не будут исчерпаны или стек future не станет пустым.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
У вас есть браузер с одной вкладкой, где вы начинаете на домашней странице и можете посетить другой URL, вернуться назад на определённое количество шагов в истории или переместиться вперёд на определённое количество шагов в истории.
Реализуйте класс BrowserHistory:
BrowserHistory(string homepage) Инициализирует объект с домашней страницей браузера.
void visit(string url) Посещает URL с текущей страницы. Это очищает всю историю вперёд.
string back(int steps) Перемещает на steps шагов назад в истории. Если вы можете вернуться только на x шагов в истории, а steps > x, вы вернётесь только на x шагов. Возвращает текущий URL после перемещения назад в истории на не более чем steps шагов.
string forward(int steps) Перемещает на steps шагов вперёд в истории. Если вы можете переместиться только на x шагов вперёд в истории, а steps > x, вы переместитесь только на x шагов. Возвращает текущий URL после перемещения вперёд в истории на не более чем steps шагов.
Пример:
Input:
["BrowserHistory","visit","visit","visit","back","back","forward","visit","forward","back","back"]
[["leetcode.com"],["google.com"],["facebook.com"],["youtube.com"],[1],[1],[1],["linkedin.com"],[2],[2],[7]]
Output:
[null,null,null,null,"facebook.com","google.com","facebook.com",null,"linkedin.com","google.com","leetcode.com"]
Explanation:
BrowserHistory browserHistory = new BrowserHistory("leetcode.com");
browserHistory.visit("google.com"); // You are in "leetcode.com". Visit "google.com"
browserHistory.visit("facebook.com"); // You are in "google.com". Visit "facebook.com"
browserHistory.visit("youtube.com"); // You are in "facebook.com". Visit "youtube.com"
browserHistory.back(1); // You are in "youtube.com", move back to "facebook.com" return "facebook.com"
browserHistory.back(1); // You are in "facebook.com", move back to "google.com" return "google.com"
browserHistory.forward(1); // You are in "google.com", move forward to "facebook.com" return "facebook.com"
browserHistory.visit("linkedin.com"); // You are in "facebook.com". Visit "linkedin.com"
browserHistory.forward(2); // You are in "linkedin.com", you cannot move forward any steps.
👨💻 Алгоритм:
1⃣Инициализация:
Создайте класс BrowserHistory с двумя стеками (history и future) и строковой переменной current для хранения текущего URL. Инициализируйте current с домашней страницей.
2⃣Посещение URL:
Метод visit(url) сохраняет текущий URL в стеке history, устанавливает url как текущий и очищает стек future.
3⃣Навигация назад и вперед:
Метод back(steps) перемещает текущий URL в стек future и извлекает URL из стека history, пока шаги не будут исчерпаны или стек history не станет пустым.
Метод forward(steps) перемещает текущий URL в стек history и извлекает URL из стека future, пока шаги не будут исчерпаны или стек future не станет пустым.
😎 Решение:
using System.Collections.Generic;
public class BrowserHistory {
private Stack<string> history;
private Stack<string> future;
private string current;
public BrowserHistory(string homepage) {
history = new Stack<string>();
future = new Stack<string>();
current = homepage;
}
public void Visit(string url) {
history.Push(current);
current = url;
future.Clear();
}
public string Back(int steps) {
while (steps > 0 && history.Count > 0) {
future.Push(current);
current = history.Pop();
steps--;
}
return current;
}
public string Forward(int steps) {
while (steps > 0 && future.Count > 0) {
history.Push(current);
current = future.Pop();
steps--;
}
return current;
}
}
Ставь 👍 и забирай 📚 Базу знаний
Задача: 645. Set Mismatch
Сложность: easy
У вас есть набор целых чисел s, который изначально содержит все числа от 1 до n. К сожалению, из-за какой-то ошибки одно из чисел в s продублировалось в другое число в наборе, что привело к повторению одного числа и потере другого. Вам дан целочисленный массив nums, представляющий состояние данных в этом наборе после ошибки. Найдите число, которое встречается дважды, и число, которое отсутствует, и верните их в виде массива.
Пример:
👨💻 Алгоритм:
1⃣Пройдите по массиву, используя набор для отслеживания чисел, чтобы определить дублированное число.
2⃣Определите отсутствующее число, используя сумму чисел от 1 до n и текущую сумму массива.
3⃣Верните дублированное и отсутствующее числа в виде массива.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
У вас есть набор целых чисел s, который изначально содержит все числа от 1 до n. К сожалению, из-за какой-то ошибки одно из чисел в s продублировалось в другое число в наборе, что привело к повторению одного числа и потере другого. Вам дан целочисленный массив nums, представляющий состояние данных в этом наборе после ошибки. Найдите число, которое встречается дважды, и число, которое отсутствует, и верните их в виде массива.
Пример:
Input: nums = [1,2,2,4]
Output: [2,3]
👨💻 Алгоритм:
1⃣Пройдите по массиву, используя набор для отслеживания чисел, чтобы определить дублированное число.
2⃣Определите отсутствующее число, используя сумму чисел от 1 до n и текущую сумму массива.
3⃣Верните дублированное и отсутствующее числа в виде массива.
😎 Решение:
public class Solution {
public int[] FindErrorNums(int[] nums) {
int n = nums.Length;
HashSet<int> numSet = new HashSet<int>();
int duplicate = -1;
foreach (int num in nums) {
if (!numSet.Add(num)) {
duplicate = num;
}
}
int missing = (n * (n + 1)) / 2 - numSet.Sum();
return new int[] { duplicate, missing };
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 1207. Unique Number of Occurrences
Сложность: easy
Дан массив целых чисел arr. Верните true, если количество вхождений каждого значения в массиве уникально, или false в противном случае.
Пример:
👨💻 Алгоритм:
1⃣Сохраните частоты элементов массива arr в хэш-таблице freq.
2⃣Итерируйтесь по хэш-таблице freq и вставьте частоты всех уникальных элементов массива arr в хэш-набор freqSet.
3⃣Верните true, если размер хэш-набора freqSet равен размеру хэш-таблицы freq, иначе верните false.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
Дан массив целых чисел arr. Верните true, если количество вхождений каждого значения в массиве уникально, или false в противном случае.
Пример:
Input: arr = [1,2,2,1,1,3]
Output: true
Explanation: The value 1 has 3 occurrences, 2 has 2 and 3 has 1. No two values have the same number of occurrences.
👨💻 Алгоритм:
1⃣Сохраните частоты элементов массива arr в хэш-таблице freq.
2⃣Итерируйтесь по хэш-таблице freq и вставьте частоты всех уникальных элементов массива arr в хэш-набор freqSet.
3⃣Верните true, если размер хэш-набора freqSet равен размеру хэш-таблицы freq, иначе верните false.
😎 Решение:
using System;
using System.Collections.Generic;
public class Solution {
public bool UniqueOccurrences(int[] arr) {
Dictionary<int, int> freq = new Dictionary<int, int>();
foreach (int num in arr) {
if (freq.ContainsKey(num)) {
freq[num]++;
} else {
freq[num] = 1;
}
}
HashSet<int> freqSet = new HashSet<int>(freq.Values);
return freq.Count == freqSet.Count;
}
}
Ставь 👍 и забирай 📚 Базу знаний
Задача: 934. Shortest Bridge
Сложность: medium
Вам дана двоичная матричная сетка n x n, где 1 обозначает сушу, а 0 - воду. Остров - это 4-направленно связанная группа из 1, не соединенная ни с одной другой 1. В сетке ровно два острова. Вы можете поменять 0 на 1, чтобы соединить два острова в один. Верните наименьшее количество 0, которое нужно перевернуть, чтобы соединить два острова.
Пример:
👨💻 Алгоритм:
1⃣Найти все клетки, принадлежащие первому острову, используя DFS/BFS, и добавить их в очередь для последующего расширения.
2⃣Использовать BFS для расширения из каждой клетки первого острова, чтобы найти кратчайший путь к клетке второго острова.
3⃣Вернуть длину кратчайшего пути.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Вам дана двоичная матричная сетка n x n, где 1 обозначает сушу, а 0 - воду. Остров - это 4-направленно связанная группа из 1, не соединенная ни с одной другой 1. В сетке ровно два острова. Вы можете поменять 0 на 1, чтобы соединить два острова в один. Верните наименьшее количество 0, которое нужно перевернуть, чтобы соединить два острова.
Пример:
Input: grid = [[0,1],[1,0]]
Output: 1
👨💻 Алгоритм:
1⃣Найти все клетки, принадлежащие первому острову, используя DFS/BFS, и добавить их в очередь для последующего расширения.
2⃣Использовать BFS для расширения из каждой клетки первого острова, чтобы найти кратчайший путь к клетке второго острова.
3⃣Вернуть длину кратчайшего пути.
😎 Решение:
using System;
using System.Collections.Generic;
public class Solution {
public int ShortestBridge(int[][] grid) {
int n = grid.Length;
var directions = new int[][] {
new int[] {-1, 0},
new int[] {1, 0},
new int[] {0, -1},
new int[] {0, 1}
};
var queue = new Queue<int[]>();
bool found = false;
for (int i = 0; i < n && !found; ++i) {
for (int j = 0; j < n && !found; ++j) {
if (grid[i][j] == 1) {
Dfs(grid, i, j, queue);
found = true;
}
}
}
int steps = 0;
while (queue.Count > 0) {
int size = queue.Count;
for (int i = 0; i < size; ++i) {
var point = queue.Dequeue();
int x = point[0], y = point[1];
foreach (var dir in directions) {
int nx = x + dir[0], ny = y + dir[1];
if (nx >= 0 && nx < n && ny >= 0 && ny < n) {
if (grid[nx][ny] == 1) {
return steps;
}
if (grid[nx][ny] == 0) {
grid[nx][ny] = -1;
queue.Enqueue(new int[] {nx, ny});
}
}
}
}
steps++;
}
return -1;
}
private void Dfs(int[][] grid, int x, int y, Queue<int[]> queue) {
int n = grid.Length;
if (x < 0 || x >= n || y < 0 || y >= n || grid[x][y] != 1) {
return;
}
grid[x][y] = -1;
queue.Enqueue(new int[] {x, y});
Dfs(grid, x - 1, y, queue);
Dfs(grid, x + 1, y, queue);
Dfs(grid, x, y - 1, queue);
Dfs(grid, x, y + 1, queue);
}
}
Ставь 👍 и забирай 📚 Базу знаний
Задача: 1266. Minimum Time Visiting All Points
Сложность: easy
На двумерной плоскости имеется n точек с целочисленными координатами points[i] = [xi, yi]. Верните минимальное время в секундах для посещения всех точек в порядке, заданном точками. Вы можете перемещаться по следующим правилам: за 1 секунду вы можете либо: переместиться по вертикали на одну единицу, по горизонтали на одну единицу, либо по диагонали sqrt(2) единиц (другими словами, переместиться на одну единицу по вертикали и на одну единицу по горизонтали за 1 секунду). Вы должны посетить точки в том же порядке, в котором они появляются в массиве. Вы можете проходить через точки, которые появляются позже в порядке, но они не считаются за посещение.
Пример:
👨💻 Алгоритм:
1⃣Инициализируйте переменную для хранения минимального времени.
2⃣Последовательно проходите по всем точкам и вычисляйте минимальное время для перехода от текущей точки к следующей.
3⃣Суммируйте время переходов для получения общего минимального времени.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
На двумерной плоскости имеется n точек с целочисленными координатами points[i] = [xi, yi]. Верните минимальное время в секундах для посещения всех точек в порядке, заданном точками. Вы можете перемещаться по следующим правилам: за 1 секунду вы можете либо: переместиться по вертикали на одну единицу, по горизонтали на одну единицу, либо по диагонали sqrt(2) единиц (другими словами, переместиться на одну единицу по вертикали и на одну единицу по горизонтали за 1 секунду). Вы должны посетить точки в том же порядке, в котором они появляются в массиве. Вы можете проходить через точки, которые появляются позже в порядке, но они не считаются за посещение.
Пример:
Input: points = [[1,1],[3,4],[-1,0]]
Output: 7
👨💻 Алгоритм:
1⃣Инициализируйте переменную для хранения минимального времени.
2⃣Последовательно проходите по всем точкам и вычисляйте минимальное время для перехода от текущей точки к следующей.
3⃣Суммируйте время переходов для получения общего минимального времени.
😎 Решение:
public class Solution {
public int MinTimeToVisitAllPoints(int[][] points) {
int time = 0;
for (int i = 0; i < points.Length - 1; i++) {
time += Distance(points[i], points[i + 1]);
}
return time;
}
private int Distance(int[] p1, int[] p2) {
return Math.Max(Math.Abs(p1[0] - p2[0]), Math.Abs(p1[1] - p2[1]));
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 443. String Compression
Сложность: medium
Дан массив символов chars, сжать его, используя следующий алгоритм:
Начните с пустой строки s. Для каждой группы последовательных повторяющихся символов в chars:
Если длина группы равна 1, добавьте символ к строке s.
В противном случае добавьте символ, а за ним длину группы.
Сжатая строка s не должна возвращаться отдельно, а вместо этого должна быть сохранена в входном массиве символов chars. Обратите внимание, что длины групп, которые равны 10 или более, будут разделены на несколько символов в chars.
После того как вы закончите модификацию входного массива, верните новую длину массива.
Вы должны написать алгоритм, который использует только постоянное дополнительное пространство.
Пример:
👨💻 Алгоритм:
1⃣Объявите переменные i – первый индекс текущей группы, и res – длина ответа (сжатой строки). Инициализируйте i = 0, res = 0.
2⃣Пока i меньше длины chars: Найдите длину текущей группы последовательных повторяющихся символов groupLength. Добавьте chars[i] к ответу (chars[res++] = chars[i]). Если groupLength > 1, добавьте строковое представление groupLength к ответу и увеличьте res соответственно. Увеличьте i на groupLength и перейдите к следующей группе.
3⃣Верните res.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дан массив символов chars, сжать его, используя следующий алгоритм:
Начните с пустой строки s. Для каждой группы последовательных повторяющихся символов в chars:
Если длина группы равна 1, добавьте символ к строке s.
В противном случае добавьте символ, а за ним длину группы.
Сжатая строка s не должна возвращаться отдельно, а вместо этого должна быть сохранена в входном массиве символов chars. Обратите внимание, что длины групп, которые равны 10 или более, будут разделены на несколько символов в chars.
После того как вы закончите модификацию входного массива, верните новую длину массива.
Вы должны написать алгоритм, который использует только постоянное дополнительное пространство.
Пример:
Input: chars = ["a","a","b","b","c","c","c"]
Output: Return 6, and the first 6 characters of the input array should be: ["a","2","b","2","c","3"]
Explanation: The groups are "aa", "bb", and "ccc". This compresses to "a2b2c3".
👨💻 Алгоритм:
1⃣Объявите переменные i – первый индекс текущей группы, и res – длина ответа (сжатой строки). Инициализируйте i = 0, res = 0.
2⃣Пока i меньше длины chars: Найдите длину текущей группы последовательных повторяющихся символов groupLength. Добавьте chars[i] к ответу (chars[res++] = chars[i]). Если groupLength > 1, добавьте строковое представление groupLength к ответу и увеличьте res соответственно. Увеличьте i на groupLength и перейдите к следующей группе.
3⃣Верните res.
😎 Решение:
public class Solution {
public int Compress(char[] chars) {
int i = 0, res = 0;
while (i < chars.Length) {
int groupLength = 1;
while (i + groupLength < chars.Length && chars[i + groupLength] == chars[i]) {
groupLength++;
}
chars[res++] = chars[i];
if (groupLength > 1) {
foreach (char ch in groupLength.ToString()) {
chars[res++] = ch;
}
}
i += groupLength;
}
return res;
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 1198. Find Smallest Common Element in All Rows
Сложность: medium
Дана матрица mat размером m x n, где каждая строка отсортирована в строго возрастающем порядке. Верните наименьший общий элемент во всех строках.
Если общего элемента нет, верните -1.
Пример:
👨💻 Алгоритм:
1⃣Инициализация переменных:
Инициализируйте массив позиций pos, переменную для текущего максимального значения cur_max и счетчик cnt нулями.
2⃣Итерация по строкам матрицы:
Для каждой строки:
- увеличивайте позицию в строке, пока значение не станет равным или больше текущего максимума.
- если достигли конца строки, возвращайте -1.
- если значение равно текущему максимуму, увеличивайте счетчик.
- в противном случае, сбросьте счетчик до 1 и обновите текущий максимум.
3⃣Проверка счетчика:
Если счетчик равен количеству строк, возвращайте текущий максимум.
Повторите шаг 2.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дана матрица mat размером m x n, где каждая строка отсортирована в строго возрастающем порядке. Верните наименьший общий элемент во всех строках.
Если общего элемента нет, верните -1.
Пример:
Input: mat = [[1,2,3,4,5],[2,4,5,8,10],[3,5,7,9,11],[1,3,5,7,9]]
Output: 5
👨💻 Алгоритм:
1⃣Инициализация переменных:
Инициализируйте массив позиций pos, переменную для текущего максимального значения cur_max и счетчик cnt нулями.
2⃣Итерация по строкам матрицы:
Для каждой строки:
- увеличивайте позицию в строке, пока значение не станет равным или больше текущего максимума.
- если достигли конца строки, возвращайте -1.
- если значение равно текущему максимуму, увеличивайте счетчик.
- в противном случае, сбросьте счетчик до 1 и обновите текущий максимум.
3⃣Проверка счетчика:
Если счетчик равен количеству строк, возвращайте текущий максимум.
Повторите шаг 2.
😎 Решение:
public class Solution {
public int SmallestCommonElement(int[][] mat) {
int n = mat.Length, m = mat[0].Length;
int[] pos = new int[n];
int cur_max = 0, cnt = 0;
while (true) {
for (int i = 0; i < n; ++i) {
while (pos[i] < m && mat[i][pos[i]] < cur_max) {
++pos[i];
}
if (pos[i] >= m) {
return -1;
}
if (mat[i][pos[i]] != cur_max) {
cnt = 1;
cur_max = mat[i][pos[i]];
} else if (++cnt == n) {
return cur_max;
}
}
}
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 263. Ugly Number
Сложность: easy
Уродливое число — это положительное целое число, простые множители которого ограничены числами 2, 3 и 5.
Дано целое число n, верните true, если n является уродливым числом.
Пример:
👨💻 Алгоритм:
1⃣Если данное целое число n неположительное, верните false, так как неположительное число не может быть уродливым.
2⃣Определите функцию keepDividingWhenDivisible, которая принимает два аргумента: делимое и делитель. Эта функция будет делить делимое на делитель до тех пор, пока оно делится без остатка. Функция возвращает измененное делимое. Последовательно примените эту функцию к n с делителями 2, 3 и 5.
3⃣Если после всех делений n равно 1, верните true, иначе верните false.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
Уродливое число — это положительное целое число, простые множители которого ограничены числами 2, 3 и 5.
Дано целое число n, верните true, если n является уродливым числом.
Пример:
Input: n = 6
Output: true
Explanation: 6 = 2 × 3
👨💻 Алгоритм:
1⃣Если данное целое число n неположительное, верните false, так как неположительное число не может быть уродливым.
2⃣Определите функцию keepDividingWhenDivisible, которая принимает два аргумента: делимое и делитель. Эта функция будет делить делимое на делитель до тех пор, пока оно делится без остатка. Функция возвращает измененное делимое. Последовательно примените эту функцию к n с делителями 2, 3 и 5.
3⃣Если после всех делений n равно 1, верните true, иначе верните false.
😎 Решение:
public class Solution {
public bool IsUgly(int n) {
if (n <= 0) {
return false;
}
foreach (int factor in new int[] {2, 3, 5}) {
n = KeepDividingWhenDivisible(n, factor);
}
return n == 1;
}
private int KeepDividingWhenDivisible(int dividend, int divisor) {
while (dividend % divisor == 0) {
dividend /= divisor;
}
return dividend;
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 1143. Longest Common Subsequence
Сложность: medium
Даны две строки text1 и text2. Верните длину их наибольшей общей подпоследовательности. Если общей подпоследовательности нет, верните 0.
Подпоследовательность строки — это новая строка, созданная из оригинальной строки путем удаления некоторых символов (может быть ни одного) без изменения относительного порядка оставшихся символов.
Например, "ace" является подпоследовательностью "abcde".
Общая подпоследовательность двух строк — это подпоследовательность, которая является общей для обеих строк.
Пример:
👨💻 Алгоритм:
1⃣Создайте двумерный массив memo для хранения промежуточных результатов, чтобы избежать повторных вычислений. Инициализируйте массив значением -1, чтобы указать, что эти ячейки еще не были рассчитаны.
2⃣Реализуйте рекурсивную функцию memoSolve, которая принимает два указателя на текущие позиции в text1 и text2 и возвращает длину наибольшей общей подпоследовательности для этих подстрок. Если текущие символы совпадают, добавьте 1 к результату рекурсивного вызова для следующих символов. Если не совпадают, найдите максимум между рекурсивными вызовами с измененными указателями.
3⃣Возвращайте значение memoSolve(0, 0), чтобы получить результат для всей строки.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Даны две строки text1 и text2. Верните длину их наибольшей общей подпоследовательности. Если общей подпоследовательности нет, верните 0.
Подпоследовательность строки — это новая строка, созданная из оригинальной строки путем удаления некоторых символов (может быть ни одного) без изменения относительного порядка оставшихся символов.
Например, "ace" является подпоследовательностью "abcde".
Общая подпоследовательность двух строк — это подпоследовательность, которая является общей для обеих строк.
Пример:
Input: text1 = "abcde", text2 = "ace"
Output: 3
Explanation: The longest common subsequence is "ace" and its length is 3.
👨💻 Алгоритм:
1⃣Создайте двумерный массив memo для хранения промежуточных результатов, чтобы избежать повторных вычислений. Инициализируйте массив значением -1, чтобы указать, что эти ячейки еще не были рассчитаны.
2⃣Реализуйте рекурсивную функцию memoSolve, которая принимает два указателя на текущие позиции в text1 и text2 и возвращает длину наибольшей общей подпоследовательности для этих подстрок. Если текущие символы совпадают, добавьте 1 к результату рекурсивного вызова для следующих символов. Если не совпадают, найдите максимум между рекурсивными вызовами с измененными указателями.
3⃣Возвращайте значение memoSolve(0, 0), чтобы получить результат для всей строки.
😎 Решение:
public class Solution {
private int[][] memo;
private string text1;
private string text2;
public int LongestCommonSubsequence(string text1, string text2) {
this.text1 = text1;
this.text2 = text2;
memo = new int[text1.Length + 1][];
for (int i = 0; i < text1.Length + 1; i++) {
memo[i] = new int[text2.Length + 1];
Array.Fill(memo[i], -1);
}
return MemoSolve(0, 0);
}
private int MemoSolve(int p1, int p2) {
if (p1 == text1.Length || p2 == text2.Length) return 0;
if (memo[p1][p2] != -1) return memo[p1][p2];
int answer;
if (text1[p1] == text2[p2]) {
answer = 1 + MemoSolve(p1 + 1, p2 + 1);
} else {
answer = Math.Max(MemoSolve(p1, p2 + 1), MemoSolve(p1 + 1, p2));
}
memo[p1][p2] = answer;
return answer;
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 980. Unique Paths III
Сложность: hard
Вам дан целочисленный массив grid размером m x n, где grid[i][j] может быть:
1, представляющая начальную клетку. Существует ровно одна начальная клетка.
2, представляющая конечную клетку. Существует ровно одна конечная клетка.
0, представляющая пустые клетки, по которым можно ходить.
-1, представляющая препятствия, по которым нельзя ходить.
Верните количество 4-направленных путей от начальной клетки до конечной клетки, которые проходят по каждой непересекаемой клетке ровно один раз.
Пример:
👨💻 Алгоритм:
1⃣Как видно, метод обратного отслеживания (backtracking) является методологией для решения определенного типа задач.
2⃣Для задачи обратного отслеживания можно сказать, что существует тысяча реализаций обратного отслеживания на тысячу людей, как будет видно из дальнейшей реализации.
3⃣Здесь мы просто покажем один пример реализации, следуя псевдокоду, показанному в разделе интуиции.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: hard
Вам дан целочисленный массив grid размером m x n, где grid[i][j] может быть:
1, представляющая начальную клетку. Существует ровно одна начальная клетка.
2, представляющая конечную клетку. Существует ровно одна конечная клетка.
0, представляющая пустые клетки, по которым можно ходить.
-1, представляющая препятствия, по которым нельзя ходить.
Верните количество 4-направленных путей от начальной клетки до конечной клетки, которые проходят по каждой непересекаемой клетке ровно один раз.
Пример:
Input: grid = [[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
Output: 2
Explanation: We have the following two paths:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
👨💻 Алгоритм:
1⃣Как видно, метод обратного отслеживания (backtracking) является методологией для решения определенного типа задач.
2⃣Для задачи обратного отслеживания можно сказать, что существует тысяча реализаций обратного отслеживания на тысячу людей, как будет видно из дальнейшей реализации.
3⃣Здесь мы просто покажем один пример реализации, следуя псевдокоду, показанному в разделе интуиции.
😎 Решение:
public class Solution {
private int rows, cols;
private int[,] grid;
private int pathCount;
private void Backtrack(int row, int col, int remain) {
if (grid[row, col] == 2 && remain == 1) {
pathCount += 1;
return;
}
int temp = grid[row, col];
grid[row, col] = -4;
remain -= 1;
int[] rowOffsets = {0, 0, 1, -1};
int[] colOffsets = {1, -1, 0, 0};
for (int i = 0; i < 4; ++i) {
int nextRow = row + rowOffsets[i];
int nextCol = col + colOffsets[i];
if (0 > nextRow || nextRow >= rows || 0 > nextCol || nextCol >= cols)
continue;
if (grid[nextRow, nextCol] < 0)
continue;
Backtrack(nextRow, nextCol, remain);
}
grid[row, col] = temp;
}
public int UniquePathsIII(int[,] grid) {
int nonObstacles = 0, startRow = 0, startCol = 0;
rows = grid.GetLength(0);
cols = grid.GetLength(1);
this.grid = grid;
for (int row = 0; row < rows; ++row)
for (int col = 0; col < cols; ++col) {
int cell = grid[row, col];
if (cell >= 0)
nonObstacles += 1;
if (cell == 1) {
startRow = row;
startCol = col;
}
}
pathCount = 0;
Backtrack(startRow, startCol, nonObstacles);
return pathCount;
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 253. Meeting Rooms II
Сложность: medium
Дан массив интервалов времени встреч intervals, где intervals[i] = [starti, endi]. Верните минимальное количество необходимых конференц-залов.
Пример:
👨💻 Алгоритм:
1⃣Отсортируйте встречи по времени их начала и инициализируйте мин-кучу с временем окончания первой встречи.
2⃣Для каждой последующей встречи проверьте, свободна ли комната (сравните время начала встречи с минимальным временем окончания в куче):
Если свободна, обновите время окончания этой комнаты.
Если не свободна, добавьте новое время окончания в кучу.
3⃣После обработки всех встреч размер кучи будет равен минимальному количеству необходимых комнат.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дан массив интервалов времени встреч intervals, где intervals[i] = [starti, endi]. Верните минимальное количество необходимых конференц-залов.
Пример:
Input: intervals = [[0,30],[5,10],[15,20]]
Output: 2
👨💻 Алгоритм:
1⃣Отсортируйте встречи по времени их начала и инициализируйте мин-кучу с временем окончания первой встречи.
2⃣Для каждой последующей встречи проверьте, свободна ли комната (сравните время начала встречи с минимальным временем окончания в куче):
Если свободна, обновите время окончания этой комнаты.
Если не свободна, добавьте новое время окончания в кучу.
3⃣После обработки всех встреч размер кучи будет равен минимальному количеству необходимых комнат.
😎 Решение:
using System;
using System.Collections.Generic;
public class Solution {
public int MinMeetingRooms(int[][] intervals) {
Array.Sort(intervals, (a, b) => a[0] - b[0]);
var heap = new SortedSet<int>();
heap.Add(intervals[0][1]);
for (int i = 1; i < intervals.Length; i++) {
if (intervals[i][0] >= heap.Min) {
heap.Remove(heap.Min);
}
heap.Add(intervals[i][1]);
}
return heap.Count;
}
}
Ставь 👍 и забирай 📚 Базу знаний
Задача: 300. Longest Increasing Subsequence
Сложность: medium
Дан массив целых чисел nums, верните длину самой длинной строго возрастающей подпоследовательности.
Пример:
👨💻 Алгоритм:
1⃣Инициализируйте массив dp длиной nums.length, все элементы которого равны 1. dp[i] представляет длину самой длинной возрастающей подпоследовательности, которая заканчивается элементом с индексом i.
2⃣Итерируйтесь от i = 1 до i = nums.length - 1. В каждой итерации используйте второй цикл for для итерации от j = 0 до j = i - 1 (все элементы перед i). Для каждого элемента перед i, проверьте, меньше ли этот элемент, чем nums[i]. Если да, установите dp[i] = max(dp[i], dp[j] + 1).
3⃣Верните максимальное значение из dp.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дан массив целых чисел nums, верните длину самой длинной строго возрастающей подпоследовательности.
Пример:
Input: nums = [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
👨💻 Алгоритм:
1⃣Инициализируйте массив dp длиной nums.length, все элементы которого равны 1. dp[i] представляет длину самой длинной возрастающей подпоследовательности, которая заканчивается элементом с индексом i.
2⃣Итерируйтесь от i = 1 до i = nums.length - 1. В каждой итерации используйте второй цикл for для итерации от j = 0 до j = i - 1 (все элементы перед i). Для каждого элемента перед i, проверьте, меньше ли этот элемент, чем nums[i]. Если да, установите dp[i] = max(dp[i], dp[j] + 1).
3⃣Верните максимальное значение из dp.
😎 Решение:
using System;
using System.Linq;
public class Solution {
public int LengthOfLIS(int[] nums) {
if (nums.Length == 0) return 0;
int[] dp = new int[nums.Length];
Array.Fill(dp, 1);
for (int i = 1; i < nums.Length; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.Max(dp[i], dp[j] + 1);
}
}
}
return dp.Max();
}
Ставь 👍 и забирай 📚 Базу знаний
Задача: 821. Shortest Distance to a Character
Сложность: easy
Дана строка s и символ c, который встречается в s. Верните массив целых чисел answer, где answer.length == s.length, и answer[i] - это расстояние от индекса i до ближайшего появления символа c в строке s.
Расстояние между двумя индексами i и j равно abs(i - j), где abs - это функция абсолютного значения.
Пример:
👨💻 Алгоритм:
1⃣При проходе слева направо будем запоминать индекс prev последнего символа C, который мы видели. Тогда ответ будет i - prev.
2⃣При проходе справа налево будем запоминать индекс prev последнего символа C, который мы видели. Тогда ответ будет prev - i.
3⃣Мы берем минимальное значение из этих двух ответов для создания нашего окончательного ответа.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
Дана строка s и символ c, который встречается в s. Верните массив целых чисел answer, где answer.length == s.length, и answer[i] - это расстояние от индекса i до ближайшего появления символа c в строке s.
Расстояние между двумя индексами i и j равно abs(i - j), где abs - это функция абсолютного значения.
Пример:
Input: s = "loveleetcode", c = "e"
Output: [3,2,1,0,1,0,0,1,2,2,1,0]
Explanation: The character 'e' appears at indices 3, 5, 6, and 11 (0-indexed).
The closest occurrence of 'e' for index 0 is at index 3, so the distance is abs(0 - 3) = 3.
The closest occurrence of 'e' for index 1 is at index 3, so the distance is abs(1 - 3) = 2.
For index 4, there is a tie between the 'e' at index 3 and the 'e' at index 5, but the distance is still the same: abs(4 - 3) == abs(4 - 5) = 1.
The closest occurrence of 'e' for index 8 is at index 6, so the distance is abs(8 - 6) = 2.
👨💻 Алгоритм:
1⃣При проходе слева направо будем запоминать индекс prev последнего символа C, который мы видели. Тогда ответ будет i - prev.
2⃣При проходе справа налево будем запоминать индекс prev последнего символа C, который мы видели. Тогда ответ будет prev - i.
3⃣Мы берем минимальное значение из этих двух ответов для создания нашего окончательного ответа.
😎 Решение:
public class Solution {
public int[] ShortestToChar(string S, char C) {
int N = S.Length;
int[] ans = new int[N];
int prev = -N;
for (int i = 0; i < N; ++i) {
if (S[i] == C) {
prev = i;
}
ans[i] = i - prev;
}
prev = 2 * N;
for (int i = N - 1; i >= 0; --i) {
if (S[i] == C) {
prev = i;
}
ans[i] = Math.Min(ans[i], prev - i);
}
return ans;
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 1342. Number of Steps to Reduce a Number to Zero
Сложность: easy
Дано целое число num, вернуть количество шагов, необходимых для его сокращения до нуля.
На каждом шаге, если текущее число четное, его нужно разделить на 2, в противном случае, вы должны вычесть из него 1.
Пример:
👨💻 Алгоритм:
1⃣На каждом шаге проверяйте, четное ли текущее число, используя оператор остатка от деления (%). Если число четное (number % 2 == 0), разделите его на 2.
2⃣Если число нечетное (number % 2 == 1), вычтите из него 1.
3⃣После выполнения каждого из этих действий увеличивайте счетчик шагов на 1, чтобы в конце вернуть его значение.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
Дано целое число num, вернуть количество шагов, необходимых для его сокращения до нуля.
На каждом шаге, если текущее число четное, его нужно разделить на 2, в противном случае, вы должны вычесть из него 1.
Пример:
Input: num = 14
Output: 6
Explanation:
Step 1) 14 is even; divide by 2 and obtain 7.
Step 2) 7 is odd; subtract 1 and obtain 6.
Step 3) 6 is even; divide by 2 and obtain 3.
Step 4) 3 is odd; subtract 1 and obtain 2.
Step 5) 2 is even; divide by 2 and obtain 1.
Step 6) 1 is odd; subtract 1 and obtain 0.
👨💻 Алгоритм:
1⃣На каждом шаге проверяйте, четное ли текущее число, используя оператор остатка от деления (%). Если число четное (number % 2 == 0), разделите его на 2.
2⃣Если число нечетное (number % 2 == 1), вычтите из него 1.
3⃣После выполнения каждого из этих действий увеличивайте счетчик шагов на 1, чтобы в конце вернуть его значение.
😎 Решение:
public int NumberOfSteps(int num) {
int steps = 0;
while (num != 0) {
if (num % 2 == 0) {
num /= 2;
} else {
num -= 1;
}
steps++;
}
return steps;
}Ставь 👍 и забирай 📚 Базу знаний
Завтра последний день!
Успей купить пожизненный easyoffer PRO - по цене 1 года
Покупаешь один раз — пользуешься всю жизнь.
👉 Акция до 31 марта: https://easyoffer.ru/pro
Успей купить пожизненный easyoffer PRO - по цене 1 года
Покупаешь один раз — пользуешься всю жизнь.
👉 Акция до 31 марта: https://easyoffer.ru/pro
Задача: 305. Number of Islands II
Сложность: hard
Дан пустой двумерный бинарный массив
Вы можете выполнить операцию "добавить землю", которая превращает воду в указанной позиции в сушу. Вам дан массив
Верните массив целых чисел
Остров окружен водой и образуется путем соединения соседних земель по горизонтали или вертикали. Вы можете считать, что все четыре края сетки окружены водой.
Пример:
👨💻 Алгоритм:
1⃣Инициализация:
Создайте массивы x[] = { -1, 1, 0, 0 } и y[] = { 0, 0, -1, 1 }, которые будут использоваться для нахождения соседей ячейки.
Создайте экземпляр UnionFind, например, dsu(m * n). Инициализируйте всех родителей значением -1. Используйте объединение по рангу, инициализируйте все ранги значением 0. Наконец, инициализируйте count = 0.
Создайте список целых чисел answer, где answer[i] будет хранить количество островов, образованных после превращения ячейки positions[i] в сушу.
2⃣Обработка позиций:
Итерация по массиву positions. Для каждой позиции в positions:
Выполните линейное отображение, чтобы преобразовать двумерную позицию ячейки в landPosition = position[0] * n + position[1].
Используйте операцию addLand(landPosition), чтобы добавить landPosition как узел в граф. Эта функция также увеличит count.
Итерация по каждому соседу позиции. Соседа можно определить с помощью neighborX = position[0] + x[i] и neighborY = position[1] + y[i], где neighborX — координата X, а neighborY — координата Y соседней ячейки. Выполните линейное отображение соседней ячейки с помощью neighborPosition = neighborX * n + neighborY. Теперь, если на neighborPosition есть суша, т.е. isLand(neighborPosition) возвращает true, выполните объединение neighborPosition и landPosition. В объединении уменьшите count на 1.
3⃣Определение количества островов:
Выполните операцию numberOfIslands, которая возвращает количество островов, образованных после превращения позиции в сушу. Добавьте это значение в answer.
Верните answer.
😎 Решение
Ставь 👍 и забирай 📚 Базу знаний
Сложность: hard
Дан пустой двумерный бинарный массив
grid размером m x n. Этот массив представляет собой карту, где 0 означает воду, а 1 — сушу. Изначально все ячейки массива — водные (т.е. все ячейки содержат 0).Вы можете выполнить операцию "добавить землю", которая превращает воду в указанной позиции в сушу. Вам дан массив
positions, где positions[i] = [ri, ci] — позиция (ri, ci), в которой следует выполнить i-ю операцию.Верните массив целых чисел
answer, где answer[i] — количество островов после превращения ячейки (ri, ci) в сушу.Остров окружен водой и образуется путем соединения соседних земель по горизонтали или вертикали. Вы можете считать, что все четыре края сетки окружены водой.
Пример:
Input: m = 1, n = 1, positions = [[0,0]]
Output: [1]
👨💻 Алгоритм:
1⃣Инициализация:
Создайте массивы x[] = { -1, 1, 0, 0 } и y[] = { 0, 0, -1, 1 }, которые будут использоваться для нахождения соседей ячейки.
Создайте экземпляр UnionFind, например, dsu(m * n). Инициализируйте всех родителей значением -1. Используйте объединение по рангу, инициализируйте все ранги значением 0. Наконец, инициализируйте count = 0.
Создайте список целых чисел answer, где answer[i] будет хранить количество островов, образованных после превращения ячейки positions[i] в сушу.
2⃣Обработка позиций:
Итерация по массиву positions. Для каждой позиции в positions:
Выполните линейное отображение, чтобы преобразовать двумерную позицию ячейки в landPosition = position[0] * n + position[1].
Используйте операцию addLand(landPosition), чтобы добавить landPosition как узел в граф. Эта функция также увеличит count.
Итерация по каждому соседу позиции. Соседа можно определить с помощью neighborX = position[0] + x[i] и neighborY = position[1] + y[i], где neighborX — координата X, а neighborY — координата Y соседней ячейки. Выполните линейное отображение соседней ячейки с помощью neighborPosition = neighborX * n + neighborY. Теперь, если на neighborPosition есть суша, т.е. isLand(neighborPosition) возвращает true, выполните объединение neighborPosition и landPosition. В объединении уменьшите count на 1.
3⃣Определение количества островов:
Выполните операцию numberOfIslands, которая возвращает количество островов, образованных после превращения позиции в сушу. Добавьте это значение в answer.
Верните answer.
😎 Решение
public class UnionFind {
private int[] parent, rank;
private int count;
public UnionFind(int size) { parent = new int[size]; rank = new int[size]; Array.Fill(parent, -1); count = 0; }
public void AddLand(int x) { if (parent[x] < 0) { parent[x] = x; count++; } }
public bool IsLand(int x) { return parent[x] >= 0; }
public int NumberOfIslands() { return count; }
public int Find(int x) { return parent[x] != x ? parent[x] = Find(parent[x]) : x; }
public void UnionSet(int x, int y) { int xset = Find(x), yset = Find(y); if (xset != yset) {
if (rank[xset] < rank[yset]) parent[xset] = yset; else { parent[yset] = xset; if (rank[xset] == rank[yset]) rank[xset]++; } count--; } }
}
public class Solution {
public IList<int> NumIslands2(int m, int n, int[][] positions) {
var dsu = new UnionFind(m * n);
int[] x = { -1, 1, 0, 0 }, y = { 0, 0, -1, 1 };
var answer = new List<int>();
foreach (var pos in positions) {
int land = pos[0] * n + pos[1];
dsu.AddLand(land);
for (int i = 0; i < 4; ++i) {
int nx = pos[0] + x[i], ny = pos[1] + y[i], neighbor = nx * n + ny;
if (nx >= 0 && nx < m && ny >= 0 && ny < n && dsu.IsLand(neighbor)) dsu.UnionSet(land, neighbor);
}
answer.Add(dsu.NumberOfIslands());
}
return answer;
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 656. Coin Path
Сложность: hard
Вам дан целочисленный массив монет (1-индексированный) длины n и целое число maxJump. Вы можете перейти на любой индекс i массива coins, если coins[i] != -1 и вы должны заплатить coins[i] при посещении индекса i. Кроме того, если вы в данный момент находитесь на индексе i, вы можете перейти только на любой индекс i + k, где i + k <= n и k - значение в диапазоне [1, maxJump]. Изначально вы находитесь на индексе 1 (coins[1] не -1). Вы хотите найти путь, который достигнет индекса n с минимальной стоимостью. Верните целочисленный массив индексов, которые вы посетите в таком порядке, чтобы достичь индекса n с минимальной стоимостью. Если существует несколько путей с одинаковой стоимостью, верните лексикографически наименьший такой путь. Если невозможно достичь индекса n, возвращается пустой массив. Путь p1 = [Pa1, Pa2, ..., Pax] длины x лексикографически меньше, чем p2 = [Pb1, Pb2, ..., Pbx] длины y, если и только если при первом j, где Paj и Pbj отличаются, Paj < Pbj; если такого j нет, то x < y.
Пример:
👨💻 Алгоритм:
1⃣Используйте динамическое программирование для нахождения минимальной стоимости до каждого индекса, начиная с первого.
2⃣Храните путь до каждого индекса для отслеживания наименьшего лексикографического пути.
3⃣Используя полученную информацию, восстановите путь с минимальной стоимостью до последнего индекса.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: hard
Вам дан целочисленный массив монет (1-индексированный) длины n и целое число maxJump. Вы можете перейти на любой индекс i массива coins, если coins[i] != -1 и вы должны заплатить coins[i] при посещении индекса i. Кроме того, если вы в данный момент находитесь на индексе i, вы можете перейти только на любой индекс i + k, где i + k <= n и k - значение в диапазоне [1, maxJump]. Изначально вы находитесь на индексе 1 (coins[1] не -1). Вы хотите найти путь, который достигнет индекса n с минимальной стоимостью. Верните целочисленный массив индексов, которые вы посетите в таком порядке, чтобы достичь индекса n с минимальной стоимостью. Если существует несколько путей с одинаковой стоимостью, верните лексикографически наименьший такой путь. Если невозможно достичь индекса n, возвращается пустой массив. Путь p1 = [Pa1, Pa2, ..., Pax] длины x лексикографически меньше, чем p2 = [Pb1, Pb2, ..., Pbx] длины y, если и только если при первом j, где Paj и Pbj отличаются, Paj < Pbj; если такого j нет, то x < y.
Пример:
Input: coins = [1,2,4,-1,2], maxJump = 2
Output: [1,3,5]
👨💻 Алгоритм:
1⃣Используйте динамическое программирование для нахождения минимальной стоимости до каждого индекса, начиная с первого.
2⃣Храните путь до каждого индекса для отслеживания наименьшего лексикографического пути.
3⃣Используя полученную информацию, восстановите путь с минимальной стоимостью до последнего индекса.
😎 Решение:
using System;
using System.Collections.Generic;
public class Solution {
public List<int> MinCostPath(int[] coins, int maxJump) {
int n = coins.Length;
if (coins[0] == -1) return new List<int>();
int[] dp = new int[n];
Array.Fill(dp, int.MaxValue);
dp[0] = coins[0];
List<int>[] path = new List<int>[n];
for (int i = 0; i < n; i++) path[i] = new List<int>();
path[0].Add(1);
PriorityQueue<(int cost, int index), int> heap = new PriorityQueue<(int, int), int>();
heap.Enqueue((coins[0], 0), coins[0]);
while (heap.Count > 0) {
var (currentCost, i) = heap.Dequeue();
if (currentCost > dp[i]) continue;
for (int k = 1; k <= maxJump; k++) {
if (i + k < n && coins[i + k] != -1) {
int newCost = currentCost + coins[i + k];
if (newCost < dp[i + k] || (newCost == dp[i + k] && ComparePaths(path[i], path[i + k], i + k + 1))) {
dp[i + k] = newCost;
path[i + k] = new List<int>(path[i]);
path[i + k].Add(i + k + 1);
heap.Enqueue((newCost, i + k), newCost);
}
}
}
}
return dp[n - 1] == int.MaxValue ? new List<int>() : path[n - 1];
}
private bool ComparePaths(List<int> path1, List<int> path2, int newIndex) {
List<int> newPath1 = new List<int>(path1);
newPath1.Add(newIndex);
return string.Join(",", newPath1).CompareTo(string.Join(",", path2)) < 0;
}
public static void Main(string[] args) {
Solution solution = new Solution();
int[] coins = { 0, 2, 4, -1, 2, 5 };
int maxJump = 2;
var result = solution.MinCostPath(coins, maxJump);
}
}
Ставь 👍 и забирай 📚 Базу знаний
Задача: 537. Complex Number Multiplication
Сложность: medium
Комплексное число можно представить в виде строки в формате "real+imaginaryi", где:
real — это действительная часть и является целым числом в диапазоне [-100, 100].
imaginary — это мнимая часть и является целым числом в диапазоне [-100, 100].
i^2 == -1.
Даны два комплексных числа num1 и num2 в виде строк, верните строку комплексного числа, представляющую их произведение.
Пример:
👨💻 Алгоритм:
1⃣ Извлечение реальной и мнимой частей:
Разделите строки a и b на реальные и мнимые части, используя символы '+' и 'i'.
2⃣ Вычисление произведения:
Переведите извлечённые части в целые числа.
Используйте формулу для умножения комплексных чисел: (a+ib)×(x+iy)=ax−by+i(bx+ay).
3⃣ Формирование строки результата:
Создайте строку в требуемом формате с реальной и мнимой частями произведения и верните её.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Комплексное число можно представить в виде строки в формате "real+imaginaryi", где:
real — это действительная часть и является целым числом в диапазоне [-100, 100].
imaginary — это мнимая часть и является целым числом в диапазоне [-100, 100].
i^2 == -1.
Даны два комплексных числа num1 и num2 в виде строк, верните строку комплексного числа, представляющую их произведение.
Пример:
Input: num1 = "1+1i", num2 = "1+1i"
Output: "0+2i"
Explanation: (1 + i) * (1 + i) = 1 + i2 + 2 * i = 2i, and you need convert it to the form of 0+2i.
👨💻 Алгоритм:
1⃣ Извлечение реальной и мнимой частей:
Разделите строки a и b на реальные и мнимые части, используя символы '+' и 'i'.
2⃣ Вычисление произведения:
Переведите извлечённые части в целые числа.
Используйте формулу для умножения комплексных чисел: (a+ib)×(x+iy)=ax−by+i(bx+ay).
3⃣ Формирование строки результата:
Создайте строку в требуемом формате с реальной и мнимой частями произведения и верните её.
😎 Решение:
public class Solution {
public string ComplexNumberMultiply(string a, string b) {
var x = a.Split(new char[] { '+', 'i' });
var y = b.Split(new char[] { '+', 'i' });
int a_real = int.Parse(x[0]);
int a_img = int.Parse(x[1]);
int b_real = int.Parse(y[0]);
int b_img = int.Parse(y[1]);
int realPart = a_real * b_real - a_img * b_img;
int imaginaryPart = a_real * b_img + a_img * b_real;
return realPart + "+" + imaginaryPart + "i";
}
}Ставь 👍 и забирай 📚 Базу знаний
Задача: 1216. Valid Palindrome III
Сложность: hard
Дана строка s и целое число k. Верните true, если s является k-палиндромом.
Строка является k-палиндромом, если её можно преобразовать в палиндром, удалив из неё не более k символов.
Пример:
👨💻 Алгоритм:
1⃣Инициализируйте двухмерный массив memo для хранения промежуточных результатов, чтобы избежать повторных вычислений. Определите функцию isValidPalindrome, которая будет возвращать минимальное количество удалений для создания палиндрома в подстроке от индекса i до j.
2⃣Реализуйте базовые случаи для функции isValidPalindrome: если i равно j, то это уже палиндром, если i и j - соседние индексы, то возвращается 1, если символы не совпадают. Если значение для пары индексов уже рассчитано, то возвращается сохраненное значение из memo.
3⃣Реализуйте основные случаи рекурсивного вычисления: если символы на позициях i и j совпадают, продолжайте проверку для подстроки без этих символов. В противном случае, рассмотрите два варианта удаления символов и выберите минимальное количество удалений, добавив 1 за текущее удаление.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: hard
Дана строка s и целое число k. Верните true, если s является k-палиндромом.
Строка является k-палиндромом, если её можно преобразовать в палиндром, удалив из неё не более k символов.
Пример:
Input: s = "abcdeca", k = 2
Output: true
Explanation: Remove 'b' and 'e' characters.
👨💻 Алгоритм:
1⃣Инициализируйте двухмерный массив memo для хранения промежуточных результатов, чтобы избежать повторных вычислений. Определите функцию isValidPalindrome, которая будет возвращать минимальное количество удалений для создания палиндрома в подстроке от индекса i до j.
2⃣Реализуйте базовые случаи для функции isValidPalindrome: если i равно j, то это уже палиндром, если i и j - соседние индексы, то возвращается 1, если символы не совпадают. Если значение для пары индексов уже рассчитано, то возвращается сохраненное значение из memo.
3⃣Реализуйте основные случаи рекурсивного вычисления: если символы на позициях i и j совпадают, продолжайте проверку для подстроки без этих символов. В противном случае, рассмотрите два варианта удаления символов и выберите минимальное количество удалений, добавив 1 за текущее удаление.
😎 Решение:
public class Solution {
private int[,] memo;
private int IsValidPalindromeHelper(char[] s, int i, int j) {
if (i == j) return 0;
if (i == j - 1) return s[i] != s[j] ? 1 : 0;
if (memo[i, j] != -1) return memo[i, j];
if (s[i] == s[j]) {
memo[i, j] = IsValidPalindromeHelper(s, i + 1, j - 1);
} else {
memo[i, j] = 1 + Math.Min(IsValidPalindromeHelper(s, i + 1, j), IsValidPalindromeHelper(s, i, j - 1));
}
return memo[i, j];
}
public bool IsValidPalindrome(string s, int k) {
int n = s.Length;
memo = new int[n, n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
memo[i, j] = -1;
}
}
return IsValidPalindromeHelper(s.ToCharArray(), 0, n - 1) <= k;
}
}Ставь 👍 и забирай 📚 Базу знаний
👍1