Название задания
Описание
Необходимо определить является ли матрица 9 x 9
правильно заполненым полем судоку. При проверке учитывать только заполненые цифрами ячейки. Правильной считается судоку в которой выполняются все правила:
- Каждая строка содержит числа
1-9
без повторений. - Каждая колонка содержит числа
1-9
без повторений. - Каждый
3-3
под-квадрат содержит числа1-9
без повторений.
Дополнительно:
- Судоку может быть (частично заполнена) коректной, но не обязательно иметь решение.
- Необходимо проверять только ячейки заполненые цифрами, ячейки заполеные "." необходимо считать пустыми.
Пример 1:
Входные данные: board =
Выходные данные: false[["8","3",".",".","7",".",".",".","."] ,["6",".",".","1","9","5",".",".","."] ,[".","9","8",".",".",".",".","6","."] ,["8",".",".",".","6",".",".",".","3"] ,["4",".",".","8",".","3",".",".","1"] ,["7",".",".",".","2",".",".",".","6"] ,[".","6",".",".",".",".","2","8","."] ,[".",".",".","4","1","9",".",".","5"] ,[".",".",".",".","8",".",".","7","9"]]
Объяснение: число 8 в левом верхнем углу дублируется в колонке и под-квадрате
Пример 2:
Входные данные: board =
Выходные данные: true[["5","3",".",".","7",".",".",".","."] ,["6",".",".","1","9","5",".",".","."] ,[".","9","8",".",".",".",".","6","."] ,["8",".",".",".","6",".",".",".","3"] ,["4",".",".","8",".","3",".",".","1"] ,["7",".",".",".","2",".",".",".","6"] ,[".","6",".",".",".",".","2","8","."] ,[".",".",".","4","1","9",".",".","5"] ,[".",".",".",".","8",".",".","7","9"]]
Пример 3:
Входные данные: board =
Выходные данные: true[["1","2","3","4","5","6","7","8","9"] ,["7","8","9","1","2","3","4","5","6"] ,["4","5","6","7","8","9","1","2","3"] ,["9","1","2","3","4","5","6","7","8"] ,["6","7","8","9","1","2","3","4","5"] ,["3","4","5","6","7","8","9","1","2"] ,["8","9","1","2","3","4","5","6","7"] ,["5","6","7","8","9","1","2","3","4"] ,["2","3","4","5","6","7","8","9","1"]]
Идея решения
Нам необходимо собрать по 9 массивов, представляющие строки, колонки и под-квадраты. В каждом из массивов не должно быть дупликатов. Для примера 2(с картинкой) первой строкой был бы массив [5, 3, 7]
, поскольку в первой строке встречаются только эти числа. Так как в первой строке дубликатов нет, можно переходить к следующей строке. Соответственно, первой колонкой(столбцом) будет [5, 6, 8, 4, 7]
, а под-квадратом будет [5, 3, 6, 9, 8]
.
В самом простом варианте, мы несколько раз пройдём по полученому board
для получения строк, колонок и под-квадратов. Затем пройдём по этим данным и проверим, что в каждом отдельном массиве нет дубликатов.
Вот несколько особеностей, которые могут помочь улучшить алгоритм:
* Данные гарантированно консистенты, board
- всегда массив с 9 вложенными массивами с 9 значениями.
* Достаточно найти первое нарушение "корректности" судоку.
* По нумерации строки и колонки можно легко определить к какому подквадрату относится ячейка.
* Множества не могут хранить дубликаты и позволяют проверять вхождение значения за константное время.
Алгоритм решения
Эффективным решением будет проверять дубликаты сразу при проходе по board
. Необходимо выделить место для хранения всех наборов чисел, для этого можно использовать хеш-таблицы, со значениями в виде множеств, которые будет вложены в общую хеш-таблицу. В случае, если в ряду, столбце или под-квадрате мы встречаем дубликат, то мы прерываем выполнение функции возвратом значения False
.
Таким образом наш алгоритм выглядит так:
1. Выделяем необходимые структуры для хранения.
2. Итерируемся по всем строкам и вложенно по каждому значению.
3. Если значение является числом проверяем вхождение в текущий ряд, столбец и под-квадрат.
4. Если после перебора всего судоку небыло дубликатов - необходимо вернуть True
.
Сложность
- По времени: \(O(N^2)\) n = len(board), учитывая консистентность данных \(O(1)\)
В ходе алгоритма мы перебираем весь полученный массив
board
и каждое значение вложенных массивов. Так как размер вложенных массивов равен размеруboard
то в худшем случае, когда мы вернёмtrue
потребуетсяn * n
времени.
Однако, следует отметить, что все значения размеров известны заранее, так число n
является константным(всегда равно 9). В следствии чего можно считать время работы скрипта константным и не превышающим конкретное количество действий.
- По памяти: \(O(N)\) n = len(board), учитывая консистентность данных \(O(1)\) Для нашего алгоритма используется 5 переменных, которые неизменяются во время работы алгоритма, затраты по памяти константны.
Так же мы выделяем хеш-таблицу, содержащую три вложенные хэш-таблицы, которые в свою очередь содержат множества, размер которых определяется количеством чисел в строках Судоку. В лучшем случае, когда таблица судоку пуста множества останутся пустыми. В худшем случае нам потребуется хранить 3 раза по 9 массивов по 9 чисел(243 числа). Учитывая, что мы знаем конкретное число, больше которого не может выделиться память, мы можем выделить её заранее и сложность будет константной, то есть \(O(1)\).
Код решения:
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
rows, cols, recs, size, n = 0, 1, 2, 3, 9
store = {p: {k: set() for k in range(n)} for p in range(size)}
for i, row in enumerate(board):
for j, num in enumerate(row):
if num != '.':
k = j // size + i // size * size
if num in store[rows][j] | store[cols][i] | store[recs][k]:
return False
store[rows][j].add(num)
store[cols][i].add(num)
store[recs][k].add(num)
return True
use std::collections::{HashMap, HashSet};
impl Solution {
pub fn is_valid_sudoku(board: Vec<Vec<char>>) -> bool {
let (rows, cols, recs, size, n) = (0, 1, 2, 3, 9);
let mut store: HashMap<usize, HashMap<usize, HashSet<char>>> = HashMap::new();
for p in 0..size {
let mut inner_map = HashMap::new();
for k in 0..n {
inner_map.insert(k, HashSet::new());
}
store.insert(p, inner_map);
}
for (i, row) in board.iter().enumerate() {
for (j, num) in row.iter().enumerate() {
if *num != '.' {
let k = j / size + i / size * size;
if store.get(&rows).unwrap().get(&i).map_or(false, |set| set.contains(&num)) ||
store.get(&cols).unwrap().get(&j).map_or(false, |set| set.contains(&num)) ||
store.get(&recs).unwrap().get(&k).map_or(false, |set| set.contains(&num)) {
return false;
}
store.get_mut(&rows).unwrap().get_mut(&i).unwrap().insert(*num);
store.get_mut(&cols).unwrap().get_mut(&j).unwrap().insert(*num);
store.get_mut(&recs).unwrap().get_mut(&k).unwrap().insert(*num);
}
}
}
true
}
}