python做题日记(17)

第三十七题

        第三十七题是需要得到给定数独的解,可以按照暴力解数独的方法,每当遍历到一个空格位置,对该位置进行填数尝试,分别尝试1~9,检查在同行同列以及同3×3方格内是否存在相同的数字,如果存在相同的数字需要撤销刚才填入的数字,然后填入下一个数字,继续判断是否合理,如果不合理继续进行填入数字尝试,如果合理则对下一个空格处进行填入数字的尝试,因此就形成了编写代码的思路,写一个用于检查填入数字是否合理的函数,以及一个递归回溯函数,对数独进行解法。

class Solution:
    def solveSudoku(self, board: list[list[str]]) -> None:
        def is_valid(row, col, ch):
            # 检查行、列、3x3宫格是否合法
            for i in range(9):
                if board[row][i] == ch or board[i][col] == ch:
                    return False
            start_row, start_col = 3 * (row // 3), 3 * (col // 3)
            for i in range(3):
                for j in range(3):
                    if board[start_row + i][start_col + j] == ch:
                        return False
            return True

        def backtrack():
            for i in range(9):
                for j in range(9):
                    if board[i][j] == '.':
                        for ch in map(str, range(1, 10)):
                            if is_valid(i, j, ch):
                                board[i][j] = ch
                                if backtrack():
                                    return True
                                board[i][j] = '.'
                        return False
            return True

        backtrack()

在检查函数中,就是简单的对于行,列以及同方格内的元素进行遍历,检查是否存在相同的数字,如果存在相同的数字则返回False,否则通过检查,返回True。对于递归回溯函数就是遍历数独,找到空格位置,对1~9数字进行循环填入尝试,没填入一个数就调入一次检查函数对其进行合理性检查,如果通过检查,则进行递归,对下一个空格进行尝试,如果发现后面的尝试过程中没有合理的数字可以填入,则撤销之前空格中填入的数字,对下一个数字进行尝试。最终的理想情况就是遍历完了整个数独,所有数字填入都是合理的,即找到了满足该数独的解。

代码优化

        在提交之后发现运行超时,说明需要对代码进行优化,减少递归调用的次数,因此需要先对数独进行遍历,记录每行每列以及每个3×3宫格内的数字,提高递归的效率。因为记录了每行每列以及每个3×3宫格内的数字,因此检查函数就可以简化为一行代码,即检查记录的集合中是否存在相同的数字,在每次填入或撤销数字的过程中都需要对记录集合进行更新,故有以下代码:

class Solution:
    def solveSudoku(self, board: list[list[str]]) -> None:
        rows = [set() for _ in range(9)]
        cols = [set() for _ in range(9)]
        boxes = [set() for _ in range(9)]
        empties = []

        for i in range(9):
            for j in range(9):
                if board[i][j] == '.':
                    empties.append((i, j))
                else:
                    rows[i].add(board[i][j])
                    cols[j].add(board[i][j])
                    boxes[(i//3)*3 + j//3].add(board[i][j])

        def backtrack(idx=0):
            if idx == len(empties):
                return True
            i, j = empties[idx]
            box_idx = (i//3)*3 + j//3
            for ch in map(str, range(1, 10)):
                if ch not in rows[i] and ch not in cols[j] and ch not in boxes[box_idx]:
                    board[i][j] = ch
                    rows[i].add(ch)
                    cols[j].add(ch)
                    boxes[box_idx].add(ch)
                    if backtrack(idx + 1):
                        return True
                    board[i][j] = '.'
                    rows[i].remove(ch)
                    cols[j].remove(ch)
                    boxes[box_idx].remove(ch)
            return False

        backtrack()

第三十八题

        第三十八题是需要返回外观数列的第n项,每一项都是源于上一项,初始数列为1。而外观数列的意思是将数列写成每个数字出现次数加上该数字的组合,即上一个数列中有几个几,再将它们组合在一起形成这一项的外观数列。故在代码中需要遍历上一项字符串,每次遍历需要记录重复数字出现的次数,直至下一个不同的数字出现,将已经记录的数字及其出现次数加入到结果外观数列中。故有以下代码:

class Solution:
    def countAndSay(self, n: int) -> str:
        res = "1"
        for _ in range(n - 1):
            cur = ""
            i = 0
            while i < len(res):
                count = 1
                while i + 1 < len(res) and res[i] == res[i + 1]:
                    i += 1
                    count += 1
                cur += str(count) + res[i]
                i += 1
            res = cur
        return res

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值