Признак в условии
- двумерная сетка, обход по спирали, поворот, транспонирование
- «обнули строку и столбец», «поверни на месте»
- координаты и границы важнее алгоритма
Идея
Отдельного алгоритма тут нет - есть дисциплина работы с границами. Два повторяющихся приёма: четыре границы (top, bottom, left, right), которые сдвигаются после каждой пройденной стороны, и разложение на известные операции (поворот = транспонирование + разворот строк).
Шаблон
let top = 0, bottom = rows - 1, left = 0, right = cols - 1;
while (top <= bottom && left <= right) {
for (let c = left; c <= right; c++) out.push(m[top][c]);
top++;
for (let r = top; r <= bottom; r++) out.push(m[r][right]);
right--;
if (top <= bottom) { // перепроверка обязательна
for (let c = right; c >= left; c--) out.push(m[bottom][c]);
bottom--;
}
if (left <= right) {
for (let r = bottom; r >= top; r--) out.push(m[r][left]);
left++;
}
}
Проверку выхода за границы при обходе соседей всегда делают в начале функции, а не перед каждым из четырёх вызовов:
const visit = (r, c) => {
if (r < 0 || c < 0 || r >= rows || c >= cols) return;
...
};
Сложность
Обычно O(rows × cols) времени. Память O(1), если работаем на месте.
Частые ошибки
- нет повторной проверки границ перед третьей и четвёртой сторонами: в матрице из одной строки элементы задваиваются
- изменение матрицы по ходу портит ещё не просмотренные клетки (обнуление нулями)
matrix[0]без проверки на пустую матрицу- строки, созданные через
Array(n).fill([]), оказываются одной и той же ссылкой
Мнемоника
«Четыре границы, каждая сдвигается ровно один раз»
Не путать с
- Обход в глубину - заливка и подсчёт областей на сетке - это обход в глубину, где соседи считаются по смещениям
- Обход в ширину - кратчайший путь по клеткам - только в ширину: в глубину найдёт путь, но не короткий
- Префиксные суммы - сумма по прямоугольнику - двумерные префиксы, а не обход
Задачи на этот паттерн
- обход матрицы по спирали
- обнуление строк и столбцов