36. 有效的数独
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
上图是一个部分填充的有效的数独。
数独部分空格内已填入了数字,空白格用 '.' 表示。
示例 1:
输入:
[["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"]
]
输出: true
示例 2:
输入:
[ ["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"]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
说明:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 '.' 。
给定数独永远是 9x9 形式的。
fun _0036_isValidSudoku() {
println("--------_0036_isValidSudoku-------")
println(
isValidSudoku(
arrayOf(
charArrayOf('8', '3', '.', '.', '7', '.', '.', '.', '.'),
charArrayOf('6', '.', '.', '1', '9', '5', '.', '.', '.'),
charArrayOf('.', '9', '5', '.', '.', '.', '.', '6', '.'),
charArrayOf('8', '.', '.', '.', '6', '.', '.', '.', '3'),
charArrayOf('4', '.', '.', '8', '.', '3', '.', '.', '1'),
charArrayOf('7', '.', '.', '.', '2', '.', '.', '.', '6'),
charArrayOf('.', '6', '.', '.', '.', '.', '2', '8', '.'),
charArrayOf('.', '.', '.', '4', '1', '9', '.', '.', '5'),
charArrayOf('.', '.', '.', '.', '8', '.', '.', '7', '9'),
)
)
)
println(
isValidSudoku(
arrayOf(
charArrayOf('8', '3', '.', '.', '7', '.', '.', '.', '.'),
charArrayOf('6', '.', '.', '1', '9', '5', '.', '.', '.'),
charArrayOf('.', '9', '8', '.', '.', '.', '.', '6', '.'),
charArrayOf('8', '.', '.', '.', '6', '.', '.', '.', '3'),
charArrayOf('4', '.', '.', '8', '.', '3', '.', '.', '1'),
charArrayOf('7', '.', '.', '.', '2', '.', '.', '.', '6'),
charArrayOf('.', '6', '.', '.', '.', '.', '2', '8', '.'),
charArrayOf('.', '.', '.', '4', '1', '9', '.', '.', '5'),
charArrayOf('.', '.', '.', '.', '8', '.', '.', '7', '9'),
)
)
)
}
fun isValidSudoku(board: Array<CharArray>): Boolean {
val rows = Array(9) { BooleanArray(9) }
val cols = Array(9) { BooleanArray(9) }
val boxs = Array(9) { BooleanArray(9) }
for (i in board.indices) {
for (j in board[i].indices) {
if (board[i][j] != '.') {
val num = board[i][j] - '0' - 1
val k = i / 3 * 3 + j / 3
if (rows[i][num] || cols[j][num] || boxs[k][num]) return false
rows[i][num] = cols[j][num]
cols[j][num] = boxs[k][num]
boxs[k][num] = true
}
}
}
return true
}
37. 解数独
编写一个程序,通过填充空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。
提示:
给定的数独序列只包含数字 1-9 和字符 '.' 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。
fun _0037_solveSudoku() {
println("--------_0037_solveSudoku-------")
solveSudoku(
arrayOf(
charArrayOf('5', '3', '.', '.', '7', '.', '.', '.', '.'),
charArrayOf('6', '.', '.', '1', '9', '5', '.', '.', '.'),
charArrayOf('.', '9', '8', '.', '.', '.', '.', '6', '.'),
charArrayOf('8', '.', '.', '.', '6', '.', '.', '.', '3'),
charArrayOf('4', '.', '.', '8', '.', '3', '.', '.', '1'),
charArrayOf('7', '.', '.', '.', '2', '.', '.', '.', '6'),
charArrayOf('.', '6', '.', '.', '.', '.', '2', '8', '.'),
charArrayOf('.', '.', '.', '4', '1', '9', '.', '.', '5'),
charArrayOf('.', '.', '.', '.', '8', '.', '.', '7', '9'),
)
)
}
fun solveSudoku(board: Array<CharArray>) {
helper(board)
for (cs in board) {
println(cs.contentToString())
}
}
fun helper(board: Array<CharArray>): Boolean {
for (i in board.indices) {
for (j in board[i].indices) {
if (board[i][j] != '.') continue
for (c in '1'..'9') {
if (!isValidSK(board, i, j, c)) continue
board[i][j] = c
if (helper(board)) return true
board[i][j] = '.'
}
return false
}
}
return true
}
fun isValidSK(board: Array<CharArray>, i: Int, j: Int, c: Char): Boolean {
for (k in 0..8) {
if (board[k][j] != '.' && board[k][j] == c) return false
if (board[i][k] != '.' && board[i][k] == c) return false
val row = i / 3 * 3 + k / 3
val col = j / 3 * 3 + k % 3
if (board[row][col] != '.' && board[row][col] == c) return false
}
return true
}
38. 外观数列
给定一个正整数 n ,输出外观数列的第 n 项。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = "1"
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
前五项如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
第一项是数字 1
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 "11"
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 "21"
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"
要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。
然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。
要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
示例 1:
输入:n = 1
输出:"1"
解释:这是一个基本样例。
示例 2:
输入:n = 4
输出:"1211"
解释:
countAndSay(1) = "1"
countAndSay(2) = 读 "1" = 一 个 1 = "11"
countAndSay(3) = 读 "11" = 二 个 1 = "21"
countAndSay(4) = 读 "21" = 一 个 2 + 一 个 1 = "12" + "11" = "1211"
提示:
1 <= n <= 30
fun _0038_countAndSay() {
println("--------_0038_countAndSay-------")
println(countAndSay(1))
println(countAndSay(2))
println(countAndSay(3))
println(countAndSay(4))
println(countAndSay(5))
println(countAndSay(6))
println(countAndSay(7))
println(countAndSay(8))
println(countAndSay(9))
}
fun countAndSay(n: Int): String {
println("countAndSay_$n:")
if (n <= 0) return ""
var res = "1"
var n = n
while (--n > 0) {
var cur = ""
var i = 0
while (i < res.length) {
var cnt = 1
while (i + 1 < res.length && res[i] == res[i + 1]) {
++cnt
++i
}
cur += cnt.toString() + res[i]
i++
}
res = cur
}
return res
}
39. 组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:
输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500
fun _0039_combinationSum() {
println("--------_0039_combinationSum-------")
println(combinationSum(intArrayOf(2, 3, 6, 7), 7))
println(combinationSum(intArrayOf(2, 3, 5), 8))
}
fun combinationSum(candidates: IntArray, target: Int): ArrayList<ArrayList<Int>> {
Arrays.sort(candidates)
val res = ArrayList<ArrayList<Int>>();
for (i in candidates.indices) {
if (candidates[i] > target) break
if (candidates[i] == target) {
res.add(arrayListOf(candidates[i]))
break
}
val vec = candidates.copyOfRange(i, candidates.size)
val tmp = combinationSum(vec, target - candidates[i])
for (a in tmp) {
a.add(0, candidates[i])
res.add(a)
}
}
return res
}
40. 组合总和 II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
fun _0040_combinationSum2() {
println("--------_0040_combinationSum2-------")
println(combinationSum2(intArrayOf(10, 1, 2, 7, 6, 1, 5), 8))
println(combinationSum2(intArrayOf(2, 5, 2, 1, 2), 5))
}
fun combinationSum2(candidates: IntArray, target: Int): ArrayList<ArrayList<Int>> {
Arrays.sort(candidates)
val res = ArrayList<ArrayList<Int>>();
for (i in candidates.indices) {
if (i > 0 && candidates[i] == candidates[i - 1]) continue
if (candidates[i] > target) break
if (candidates[i] == target) {
res.add(arrayListOf(candidates[i]))
break
}
val vec = candidates.copyOfRange(i + 1, candidates.size)
val tmp = combinationSum2(vec, target - candidates[i])
for (a in tmp) {
a.add(0, candidates[i])
res.add(a)
}
}
return res
}
我是今阳,如果想要进阶和了解更多的干货,欢迎关注公众号”今阳说“接收我的最新文章