Алгоритмы - Матрицы

Функция maximalRectangle находит площадь наибольшего прямоугольника, состоящего из единиц, в бинарной матрице.

Алгоритм решения

  1. Преобразование задачи в серию задач поиска наибольшей площади гистограммы
  2. Построение гистограммы для каждой строки матрицы
  3. Использование алгоритма поиска наибольшей площади гистограммы для каждой построенной гистограммы
  4. Обновление максимальной найденной площади
  • Функция maximalRectangle:
    • Проверяет входные данные на корректность
    • Инициализирует массив высот гистограммы
    • Итерируется по строкам матрицы, обновляя гистограмму
    • Вызывает функцию largestRectangleArea для каждой гистограммы
  • Функция largestRectangleArea:
    • Использует стек для эффективного поиска наибольшей площади в гистограмме
    • Обрабатывает каждый столбец гистограммы, вычисляя возможные прямоугольники
  • Временная сложность: O(rows * cols), где rows и cols - размеры матрицы
    • Каждый элемент матрицы обрабатывается один раз
    • Для каждой строки выполняется линейный проход по гистограмме
  • Пространственная сложность: O(cols)
    • Используется дополнительная память для хранения гистограммы
    • Стек в худшем случае может содержать все столбцы
func maximalRectangle(matrix [][]byte) int {
	if len(matrix) == 0 || len(matrix[0]) == 0 {
		return 0
	}

	rows, cols := len(matrix), len(matrix[0])
	heights := make([]int, cols)
	maxArea := 0

	for i := 0; i < rows; i++ {
		for j := 0; j < cols; j++ {
			if matrix[i][j] == '1' {
				heights[j]++
			} else {
				heights[j] = 0
			}
		}
		area := largestRectangleArea(heights)
		if area > maxArea {
			maxArea = area
		}
	}

	return maxArea
}

func largestRectangleArea(heights []int) int {
	stack := []int{}
	maxArea := 0
	heights = append(heights, 0) // добавляем 0 в конец для обработки оставшихся элементов в стеке

	for i := 0; i <= len(heights)-1; i++ {
		for len(stack) > 0 && heights[stack[len(stack)-1]] > heights[i] {
			height := heights[stack[len(stack)-1]]
			stack = stack[:len(stack)-1]

			width := i
			if len(stack) > 0 {
				width = i - stack[len(stack)-1] - 1
			}

			if height*width > maxArea {
				maxArea = height * width
			}
		}
		stack = append(stack, i)
	}

	return maxArea
}
                                        

Функция numIslands подсчитывает количество островов в двумерной сетке, где '1' представляет сушу, а '0' - воду.

Алгоритм решения

  1. Итерация по всем ячейкам сетки
  2. При обнаружении суши ('1') выполняется поиск в глубину (DFS)
  3. DFS помечает все связанные участки суши как посещенные
  4. Увеличение счетчика островов после каждого завершенного DFS
  • Функция numIslands:
    • Проверяет входные данные на корректность
    • Итерируется по всем ячейкам сетки
    • Вызывает DFS для каждого найденного участка суши
    • Подсчитывает количество обнаруженных островов
  • Функция dfs:
    • Рекурсивно обходит соседние ячейки суши
    • Помечает посещенные ячейки, изменяя их значение на '0'
    • Проверяет границы сетки и тип ячейки (суша/вода)
  • Временная сложность: O(m * n), где m - количество строк, n - количество столбцов в сетке
    • Каждая ячейка посещается не более одного раза
  • Пространственная сложность: O(m * n) в худшем случае
    • Обусловлена рекурсивным стеком DFS
    • Достигает максимума, если вся сетка заполнена сушей
func numIslands(grid [][]byte) int {
	if len(grid) == 0 || len(grid[0]) == 0 {
		return 0
	}

	count := 0
	for i := range grid {
		for j := range grid[i] {
			if grid[i][j] == '1' {
				dfs(grid, i, j)
				count++
			}
		}
	}
	return count
}

func dfs(grid [][]byte, i, j int) {
	if i < 0 || i >= len(grid) || j < 0 || j >= len(grid[0]) || grid[i][j] != '1' {
		return
	}

	// Помечаем текущую клетку как посещенную
	grid[i][j] = '0'

	// Рекурсивно обходим соседние клетки
	dfs(grid, i+1, j)
	dfs(grid, i-1, j)
	dfs(grid, i, j+1)
	dfs(grid, i, j-1)
}
                                        

Функция rotate выполняет поворот квадратной матрицы (изображения) на 90 градусов по часовой стрелке без использования дополнительной памяти.

Алгоритм решения

  1. Транспонирование матрицы (замена строк на столбцы)
  2. Отражение каждой строки матрицы
  • Транспонирование матрицы:
    • Обмен элементов matrix[i][j] и matrix[j][i] для i < j
    • Изменяет расположение элементов по диагонали
  • Отражение строк:
    • Обмен элементов в каждой строке: первый с последним, второй с предпоследним и т.д.
    • Выполняется только для половины элементов каждой строки
  • Временная сложность: O(n^2), где n - размер стороны матрицы
    • Транспонирование: O(n^2/2) операций
    • Отражение: O(n^2/2) операций
  • Пространственная сложность: O(1)
    • Все операции выполняются на месте, без использования дополнительной памяти
func rotate(matrix [][]int) {
	n := len(matrix)

	// Транспонируем матрицу (строки и столбцы меняем местами)
	for i := 0; i < n; i++ {
		for j := i; j < n; j++ {
			matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
		}
	}

	// Отражаем каждую строку
	for i := 0; i < n; i++ {
		for j := 0; j < n/2; j++ {
			matrix[i][j], matrix[i][n-1-j] = matrix[i][n-1-j], matrix[i][j]
		}
	}
}
                                        

Функция generateMatrix создает квадратную матрицу размером n x n, заполненную числами от 1 до n^2 в спиральном порядке по часовой стрелке.

Алгоритм решения

  1. Инициализация пустой матрицы размером n x n
  2. Определение границ для заполнения (left, right, top, bottom)
  3. Последовательное заполнение матрицы по спирали:
    • Верхняя строка слева направо
    • Правый столбец сверху вниз
    • Нижняя строка справа налево
    • Левый столбец снизу вверх
  4. Сужение границ после каждого прохода
  5. Повторение процесса до заполнения всей матрицы
  • Использование четырех указателей (left, right, top, bottom) для отслеживания границ
  • Применение переменной num для последовательной нумерации элементов
  • Использование вложенных циклов для заполнения каждой стороны спирали
  • Проверки условий для предотвращения повторного заполнения ячеек
  • Временная сложность: O(n^2)
    • Каждая ячейка матрицы заполняется ровно один раз
  • Пространственная сложность: O(n^2)
    • Для хранения результирующей матрицы
func generateMatrix(n int) [][]int {
    // Создаем матрицу n x n, заполненную нулями
    matrix := make([][]int, n)
    for i := range matrix {
        matrix[i] = make([]int, n)
    }

    // Определяем границы
    left, right := 0, n-1
    top, bottom := 0, n-1
    num := 1

    for left <= right && top <= bottom {
        // Заполняем верхнюю строку слева направо
        for col := left; col <= right; col++ {
            matrix[top][col] = num
            num++
        }
        top++

        // Заполняем правый столбец сверху вниз
        for row := top; row <= bottom; row++ {
            matrix[row][right] = num
            num++
        }
        right--

        if top <= bottom {
            // Заполняем нижнюю строку справа налево
            for col := right; col >= left; col-- {
                matrix[bottom][col] = num
                num++
            }
            bottom--
        }

        if left <= right {
            // Заполняем левый столбец снизу вверх
            for row := bottom; row >= top; row-- {
                matrix[row][left] = num
                num++
            }
            left++
        }
    }

    return matrix
}