leetcode题解python版:36-40

36、有效的数独
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
解:遍历即可

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        column=[[] for i in range(9)]
        row=[[] for i in range(9)]
        block=[[] for i in range(9)]
        for i in range(9):
            a=i//3
            for j in range(9):
                b=j//3
                blockNum=a*3+b
                num=board[i][j]
                if num!=".":
                    if (num not in row[i]):
                        row[i].append(num)
                    else:
                        return False
                    if (num not in column[j]):
                        column[j].append(num)
                    else:
                        return False
                    if (num not in block[blockNum]):
                        block[blockNum].append(num)
                    else:
                        return False
        return True

执行用时:48 ms, 击败了89.75%的用户
内存消耗:13.8 MB

37、解数独
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。
解:暴力回溯,先把取过值的加入row,col和block中,然后一行一行去试,用深度优先搜索,初始为(0,0)。
self.flag是可以在函数中被改变值的全局变量,可以用来判断在递归的函数里是否已经都填完了。

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        col=[[] for i in range(9)]
        row=[[] for i in range(9)]
        block=[[] for i in range(9)]
        for i in range(9):
            a=i//3
            for j in range(9):
                b=j//3
                blockNum=a*3+b
                num=board[i][j]
                if num !=".":
                    col[j].append(num)
                    row[i].append(num)
                    block[blockNum].append(num)
        
        def dfs(i,j):#一行一行去回溯
            if board[i][j]!=".":
                if i==8 and j==8:
                    self.flag=True
                    return
                if j<8:
                    dfs(i,j+1)
                else:
                    dfs(i+1,0)
                return
            a=i//3
            b=j//3
            for ch in range(1,10):
                ch=str(ch)
                if ch not in col[j] and ch not in row[i] and ch not in block[a*3+b]:
                    col[j].append(ch)
                    row[i].append(ch)
                    block[a*3+b].append(ch)
                    board[i][j] = ch
                    if i==8 and j==8:
                        self.flag=True
                        return
                    if j<8:
                        dfs(i,j+1)
                    else:
                        dfs(i+1,0)
                    if self.flag:
                        return
                    board[i][j]='.'
                    col[j].remove(ch)
                    row[i].remove(ch)
                    block[a*3+b].remove(ch)
        self.flag=False
        dfs(0,0)

执行用时:224 ms, 在击败了49.18%的用户
内存消耗:13.9 MB
注:解答中的算法可以做如下改进:先算出所有空的所有可能取值,把不同空按取值数从小到大入栈,按照栈的顺序回溯会大大加快运行速度。

38、外观数列
给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 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
解:用两个指针向右移动读取即可

class Solution:
    def countAndSay(self, n: int) -> str:
        if n==1:
            return "1"
        num=self.countAndSay(n-1)
        k=len(num)
        i=j=0
        ans=""
        while j<k:
            while num[j]==num[i]:
                j=j+1
                if j>=k:
                    break
            s=str(j-i)
            ans=ans+s+str(num[i])
            i=j
        return ans            

执行用时:52 ms, 击败了36.67%的用户
内存消耗:13.6 MB, 击败了82.39%的用户

39、组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
解:想到的是回溯,每一次target减candidates中的任意元素,一直减,如果等于0,则路径path是一个解,如果>0,继续减,如果小于0,则停止,回到上一层。因此可以用dfs来遍历各种情况。
然后就是去重复,可以设置一个取candidates元素中的起始点begin,初值为0,前面的搜索会优先搜索candidates[0],后来的所有若是还搜candidates[0]就会重复,因此可以避免重复。
选取起点begin是因为在begin分支中(target第一个减的数是candidates[begin]),含有candidates[begin]的结果已经全部被列举出来了,因此后面的分支不需要再去列举candidates[begin]

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        if not candidates:
            return []
        n=len(candidates)
        candidates.sort()
        path=[]
        ans=[]
        self.dfsq(candidates,0,n,path,ans,target)
        return ans

    def dfsq(self,candidates,begin,n,path,ans,target):#设置begin是为了防止重复
    #在搜索的时候,需要设置搜索起点的下标 begin ,由于一个数可以使用多次,下一层的结点从这个搜索起点开始搜索;在搜索起点 begin 之前的数因为以前的分支搜索过了,所以一定会产生重复。
        if target==0:
            ans.append(path[:]) 
            # Python 中可变对象是引用传递,因此需要将当前 path 里的值拷贝出来
            return
        for i in range(begin,n):
            temp=target-candidates[i]
            if temp<0:
                break
            else:
                path.append(candidates[i])
            self.dfsq(candidates, i, n, path, ans, temp)
            path.pop()#上面讨论的是加入candidates[i]的情况,要把它去掉再去重新加入

执行用时:52 ms, 击败了91.07%的用户
内存消耗:13.5 MB,击败了98.00%的用户

40、组合总和 II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
解:和上一道题思路相同,但是更加简单。不同之处是这道题里candidates中可能有重复数字。

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        if not candidates:
            return []
        candidates.sort()
        n=len(candidates)
        path=[]
        ans=[]
        self.dfs(candidates,0,n,path,target,ans)
        return ans
    
    def dfs(self,candidates,begin,n,path,target,ans):
        if target == 0:
            if path not in ans:
                ans.append(path[:])
            return
        if begin>=n:
            return
        if target==0:
            if path not in ans:
                ans.append(path[:])
            return
        for i in range(begin,n):
            temp=target-candidates[i]
            if temp<0:
                break
            else:
                path.append(candidates[i])
                self.dfs(candidates,i+1,n,path,temp,ans)
                path.pop()

执行用时:64 ms, 击败了58.26%的用户
内存消耗:13.6 MB, 击败了86.14%的用户

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值