깊은바다거북
개발 공부 기록
깊은바다거북
전체 방문자
오늘
어제
  • 분류 전체보기 (219)
    • JAVA (9)
    • JavaScript (15)
    • 스파르타코딩클럽 (11)
      • [내일배움단] 웹개발 종합반 개발일지 (5)
      • [내일배움캠프] 프로젝트와 트러블 슈팅 (6)
    • SQL | NoSQL (4)
    • CS 등등 (0)
    • TIL | WIL (173)
    • 기타 에러 해결 (3)
    • 내 살 길 궁리 (4)

인기 글

최근 글

최근 댓글

태그

  • 자바스크립트 기초 문법
  • 자료 구조
  • POST / GET 요청
  • 코딩테스트 연습문제
  • 프로그래머스
  • 최대 힙(Max Heap)
  • leetcode-cli
  • 트러블 슈팅 Troubleshooting
  • Backtracking(백트래킹)
  • Til
  • Trie
  • 재귀 함수
  • 시간 복잡도
  • Preorder Traversal(전위 순회)
  • 01. 미니 프로젝트
  • Binary Tree(이진 트리)
  • BST(이진 탐색 트리)
  • tree
  • TIT (Today I Troubleshot)
  • BFS(너비우선탐색)
  • Inorder Traversal(중위 순회)
  • Linked List
  • TypeScript
  • 자잘한 에러 해결
  • 혼자 공부하는 자바스크립트
  • 최소 힙(Min Heap)
  • Leetcode
  • DFS(깊이우선탐색)
  • 팀 프로젝트
  • 점화식(Recurrence Relation)
hELLO · Designed By 정상우.
깊은바다거북

개발 공부 기록

5/24 수 [LeetCode #36] 유효한 스도쿠 TIL
TIL | WIL

5/24 수 [LeetCode #36] 유효한 스도쿠 TIL

2023. 5. 24. 23:14

[LeetCode] 36. Valid Sudoku

Valid Sudoku - LeetCode
Can you solve this real interview question? Valid Sudoku - Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules: 1. Each row must contain the digits 1-9 without repetition. 2. Each column must contain the digits 1-9 without repetition. 3. Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition. Note: * A Sudoku board (partially filled) could be valid but is not necessarily solvable. * Only the filled cells need to be validated according to the mentioned rules.   Example 1: [https://upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Sudoku-by-L2G-20050714.svg/250px-Sudoku-by-L2G-20050714.svg.png] Input: board = [["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"]] Output: true Example 2: Input: board = [["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"]] Output: false Explanation: Same as Example 1, except with the 5 in the top left corner being modified to 8. Since there are two 8's in the top left 3x3 sub-box, it is invalid.   Constraints: * board.length == 9 * board[i].length == 9 * board[i][j] is a digit 1-9 or '.'.
https://leetcode.com/problems/valid-sudoku/description/

문제:

Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:

  1. Each row must contain the digits 1-9 without repetition.
  1. Each column must contain the digits 1-9 without repetition.
  1. Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition.

Note:

  • A Sudoku board (partially filled) could be valid but is not necessarily solvable.
  • Only the filled cells need to be validated according to the mentioned rules.

Example 1:

Input: board =
[["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"]]
Output: true

Example 2:

Input: board =
[["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"]]
Output: false
Explanation: Same as Example 1, except with the5 in the top left corner being modified to8. Since there are two 8's in the top left 3x3 sub-box, it is invalid.

Constraints:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] is a digit 1-9 or '.'.

나의 풀이:

// 행당, 열당, 3*3의 미니보드 당 각각 중복되는 1-9의 숫자가 있는지 검사하기
function isValidSudoku(board) {
	// 1. 한 row 안의 1-9 빈도수 세기 및 중복 검사
	// time: O(row * col) = O(N^2)
	for (let row = 0; row < board.length; row++) { // O(row)
		const rowDuplicateCount = new Map(); 
		for (let num of board[row]) { // O(col)
			if (num !== ".") {
				if (rowDuplicateCount.has(num)) return false;
				rowDuplicateCount.set(num, true);
			}
		}
	}

	// 2. 한 column의 1-9 빈도수 세기 및 중복 검사
	// time: O(col * row) = O(N^2)
	for (let column = 0; column < board[0].length; column++) { // O(col)
		const columnDuplicateCount = new Map();
		for (let row of board) { // O(row)
			const num = row[column];
			if (num !== ".") {
				// console.log('current culumn: ', column, ' current number: ', num);
				if (columnDuplicateCount.has(num)) return false;
				columnDuplicateCount.set(num, true);
			}
		}
	}

	// 3. 한 subSquare 안의 1-9 빈도수 세기 및 중복 검사
	// time: O(row/3) * { O(col/3) + 4O(3) } * O(9) => O(N) * { O(N) + O(1) } * O(1) => O(N^2)
	for (let i = 0; i < 9; i += 3) { // O(row/3)
		for (let j = 0; j < 9; j += 3) { // O(col/3)
			const subSquareCount = new Map();
			const subSquare = board.slice(i, i + 3).map((row) => row.slice(j, j + 3)).flat(); // O(3) + O(3) + O(3) + O(3) => O(1)
			// console.log('i, j, first subSquare : ', i, j, subSquare);
			for (let num of subSquare) { // O(9)
				if (num !== ".") {
					if (subSquareCount.has(num)) return false;
					subSquareCount.set(num, true);
				}
			}
		}
	}

	return true;
}
  • 행과 열을 도는 이중 루프를 3번 (각각 행, 렬, 3x3 검사를 위해) 사용했는데 이를 한 번의 루프 안으로 통합하는 작업을 하고 싶었다. 3x3에서 생각이 막혔는데 다른 사람의 풀이에서 바로 알 수 있었다.

다른 풀이:

// 위의 해법과 논리가 같다. 다만
// i, j 한번의 루프 안에서 행/렬/3*3을 전부 처리한 해법: 
function hashSetSolution(board) {
	for (let i = 0; i < 9; i++) {
		let row = new Set(),
			col = new Set(),
			box = new Set();
		
		/* 
		00 01 02 | 03 04 05 | 06 07 08
		10 11 12 | 13 14 15 | 16 17 18  // i = 0, 1, 2
		20 21 22 | 23 24 25 | 26 27 28
		------------------------------
		30 31 32 | 33 34 35 | 36 37 38
		40 41 42 | 43 44 45 | 46 47 48  // i = 3, 4, 5
		50 51 52 | 53 54 55 | 56 57 58
		------------------------------
		60 61 62 | 63 64 65 | 66 67 68 
		70 71 72 | 73 74 75 | 76 77 78  // i = 6, 7, 8
		80 81 82 | 83 84 85 | 86 87 88
		*/

		for (let j = 0; j < 9; j++) {
			let _row = board[i][j]; // [0][0->8]
			let _col = board[j][i]; // [0->8][0]
			let _box = board[3 * Math.floor(i / 3) + Math.floor(j / 3)][3 * (i % 3) + (j % 3)];
			// i=0, j=0->2일 때, board[3 * 0 + 0][3 * 0 + 0->2] = board[0][0->2]
			// i=0, j=3->5일 때, board[3 * 0 + 1][3 * 0 + 0->2] = board[1][0->2]
			// i=0, j=6->8일 때, board[3 * 0 + 2][3 * 0 + 0->2] = board[2][0->2]
			// 
			// i=1, j=0->2일 때, board[3 * 0 + 0][3 * 1 + 0->2] = board[0][3->5]
			// i=1, j=3->5일 때, board[3 * 0 + 1][3 * 1 + 0->2] = board[1][3->5]
			// i=1, j=6->8일 때, board[3 * 0 + 2][3 * 1 + 0->2] = board[2][3->5]
			// ...

			if (_row != '.') {
				if (row.has(_row)) return false;
				row.add(_row);
			}
			if (_col != '.') {
				if (col.has(_col)) return false;
				col.add(_col);
			}
			
			if (_box != '.') {
				if (box.has(_box)) return false;
				box.add(_box);
			} 
		}
	}

	return true;
}

// 내가 하나의 루프로 통합하려고 했던 처음의 시도:
function isValidSudoku(board) {

	// 0. 한 번의 루프로 통합
	for (let row = 0; row < board.length; row++) {
		const rowDuplicateCount = {};
		const colDuplicateCount = {};
		const subSquareDuplicateCount = {};

		for (let col = 0; col < board[0].length; col++) {
			const num = board[row][col];
			if (num !== ".") {
				if (num in rowDuplicateCount[row] || num in colDuplicateCount[row] || num in subSquareDuplicateCount[row]) {
					return false;
				}

				// store row, col duplicate info
				rowDuplicateCount[row] ? rowDuplicateCount[row][num] = true : rowDuplicateCount[row] = { num: true };
				colDuplicateCount[col] ? colDuplicateCount[col][num] = true : colDuplicateCount[col] = { num: true };
				// subsquare numbering:
				// [0 - 3][0 - 3] => 0 / [0 - 3][3 - 6] => 1 / [0 - 3][6 - 9] => 2
				// [3 - 6][0 - 3] => 3 / [3 - 6][3 - 6] => 4 / [3 - 6][6 - 9] => 5
				// [6 - 9][0 - 3] => 6 / [6 - 9][3 - 6] => 7 / [6 - 9][6 - 9] => 8
				// row: Math...
				// []...
				subSquareDuplicateCount[row] ? subSquareDuplicateCount[row][num] = true : subSquareDuplicateCount[row] = { num: true }; // 여기서 건너뜀
				...
			}
		}
	}
}

// Set을 하나만 쓰고 방금 시도하던 방식와 더 유사한 풀이: 
function singleSetSolution(board) {
	let set = new Set();
    
    for(let r = 0; r < board.length; r++){
        for(let c = 0; c < board[r].length; c++){
            let val = board[r][c];
            
            if(val === '.')
                continue;
            
            //basically, formula is 3 * row + col --> to turn 2d array into 1d array
            //using Math.floor(row/3) + Math.floor(col/3) --> so we can find row and col of our sub box 
            let boxNum = 3 * Math.floor(r/3) + Math.floor(c/3);
            
            let inRow = `row: ${r}, value: ${val}`;
            let inCol = `col: ${c}, value: ${val}`;
            let inSubBox = `subBox: ${boxNum}, value: ${val}`;
            
            if(set.has(inRow) || set.has(inCol) || set.has(inSubBox))
                return false;
            
            set.add(inRow);
            set.add(inCol);
            set.add(inSubBox);
        }
	}
	
    return true;
}

알게 된 점:

  • 2D 배열 완전 정복: i로 바깥 루프를, j로 내부 루프를 돈다고 할 때
    • 행별로 순회하기(= 바깥 루프가 한 번 돌 때 한 줄의 행을 검사하기) : board[i][j]
    • 열별로 순회하기(= 바깥 루프가 한 번 돌 때 한 줄의 열을 검사하기) : board[j][i]
    • (3 x 3)의 작은 정사각형별로 순환하기(= 바깥 루프가 한 번 돌 때 한 3 x 3 정사각형 순회하기) : board[3 * Math.floor(i / 3) + Math.floor(j / 3)][3 * (i % 3) + (j % 3)]
  • 2D 배열을 특정한 패턴으로 순회해야 할 일이 있을 때 아래와 같이 행렬 번호를 그려놓고 패턴을 찾으면 많은 도움이 된다.
    00 01 02 | 03 04 05 | 06 07 08
    10 11 12 | 13 14 15 | 16 17 18  // i = 0, 1, 2
    20 21 22 | 23 24 25 | 26 27 28
    ------------------------------
    30 31 32 | 33 34 35 | 36 37 38
    40 41 42 | 43 44 45 | 46 47 48  // i = 3, 4, 5
    50 51 52 | 53 54 55 | 56 57 58
    ------------------------------
    60 61 62 | 63 64 65 | 66 67 68 
    70 71 72 | 73 74 75 | 76 77 78  // i = 6, 7, 8
    80 81 82 | 83 84 85 | 86 87 88


※ 위 내용은 스스로 공부하며 적어둔 노트이며 불확실한 내용이 있을 수 있습니다. 학습용으로 적합하지 않음을 유념해주세요. ※


Uploaded by N2T

    'TIL | WIL' 카테고리의 다른 글
    • 8/17 (목) 오버로딩 안되는 JavaScript의 메소드와 속성 TIL
    • 8/16 (수) 링크드 리스트와 재귀함수 점화식 TIL
    • 5/22 월 [LeetCode #347] K개의 최빈값 TIL
    • 5/19 금 [LeetCode #1] 투썸 TIL
    깊은바다거북
    깊은바다거북

    티스토리툴바