美文网首页
Python小白 Leetcode刷题历程 No.36-N

Python小白 Leetcode刷题历程 No.36-N

作者: _LanXiu | 来源:发表于2020-02-04 22:41 被阅读0次

    Python小白 Leetcode刷题历程 No.36-No.40 有效的数独、解数独、外观数列、组合总和、组合总和Ⅱ

    写在前面:

    作为一个计算机院的大学生,总觉得仅仅在学校粗略的学习计算机专业课是不够的,尤其是假期大量的空档期,作为一个小白,实习也莫得路子,又不想白白耗费时间。于是选择了Leetcode这个平台来刷题库。编程我只学过基础的C语言,现在在自学Python,所以用Python3.8刷题库。现在我Python掌握的还不是很熟练,算法什么的也还没学,就先不考虑算法上的优化了,单纯以解题为目的,复杂程度什么的以后有时间再优化。计划顺序五个题写一篇日志,希望其他初学编程的人起到一些帮助,写算是对自己学习历程的一个见证了吧。

    有一起刷LeetCode的可以关注我一下,我会一直发LeetCode题库Python3解法的,也可以一起探讨。

    觉得有用的话可以点赞关注下哦,谢谢大家!
    ········································································································································································

    No.36.有效的数独

    难度:中等
    题目描述:


    在这里插入图片描述
    在这里插入图片描述

    题解代码(Python3.8)

    class Solution:
        def isValidSudoku(self, board: List[List[str]]) -> bool:
            set_box=set()
            for i in range(9):
                for j in range(9):
                    if board[i][j].isdigit():
                        row = 'row_'+str(i)+' ' + board[i][j]
                        col = 'col_'+str(j)+' ' + board[i][j]
                        small_square = 'row-col_'+str(i//3)+'-'+str(j//3)+' ' + board[i][j]
                        if  row in set_box or col in set_box or small_square in set_box:
                            return False
                        set_box.update([row,col,small_square])
            return True
    

    或许有用的知识点:
    set.add(key)可以在set中添加一个元素,set.remove(key)可以在set中删除一个元素。
    如果想在set中一次性假如多个元素,可使用set.update([key1,key2,key3])。

    解题思路:
    在数独9宫格中,每个数字必定带有三个特性:行数、列数、小方块位置。对于一个i行j列的元素n,我们不妨定义它的特性为,row=row_i n , col=col_j n , small_square=row-col_(i//3)-(j)//3 n。对于每个数,我们将他的三个特性储存到一个set中。当新一个数字的三个特性都不在set中,我们便将其写入set;如果新一个数字有特性已经在set中了,不满足条件,返回False。

    No.37.解数独

    难度:困难
    题目描述:


    在这里插入图片描述
    在这里插入图片描述

    题解代码(Python3.8)

    class Solution:
        def solveSudoku(self, board: List[List[str]]) -> None:
            row = [set(range(1,10)) for _ in range(9)]      #行剩余可用数字
            col = [set(range(1,10)) for _ in range(9)]      #列剩余可用数字
            block = [set(range(1,10)) for _ in range(9)]    #块剩余可用数字
            empty = []                                      #收集空白位置 
            for i in range(9):
                for j in range(9):
                    if board[i][j] != '.':
                        val = int(board[i][j])
                        row[i].remove(val)
                        col[j].remove(val)
                        block[(i//3)*3+(j//3)].remove(val)
                    else:
                        empty.append((i,j))
            
            def backtrack(iter_=0):
                if iter_ == len(empty):
                    return True
                i,j = empty[iter_]
                block_index = (i//3)*3+(j//3)
                for val in row[i] & col [j] & block[block_index]:
                    row[i].remove(val)
                    col[j].remove(val)
                    block[block_index].remove(val)
                    board[i][j] = str(val)
                    if backtrack(iter_+1):
                        return True
                    row[i].add(val)
                    col[j].add(val)
                    block[block_index].add(val)
                return False
    
            backtrack()
    

    或许有用的知识点:
    这道题需要使用回溯算法。
    set( range(j) ) for _ in range(i),可以制作一个i行j列的set型二维数组,例如set( range(3)) for _ in range(4) = [ {0,1,2} , {0,1,2} , {0,1,2} , {0,1,2} ]。
    要求列表A,B,C的公共元素,应使用 A & B & C ,(逻辑运算 and or not 只会返回运算的布尔值)。

    解题思路:
    先确定回溯函数,对比回溯函数模板,回溯出口为iter_长度与empty相等,回溯主体中在当发现子状态的有效解后进入下一状态,否则回溯到原来状态。
    之后初始化状态,要有行剩余可用数字、列剩余可用数字、块剩余可用数字、空白方格位置。最后调用回溯函数即可。

    No.38.外观数列

    难度:简单
    题目描述:


    在这里插入图片描述

    题解代码(Python3.8)

    class Solution:
        def countAndSay(self, n: int) -> str:
            prev_person = '1'
            for i in range(1,n):
                next_person,num,count = '',prev_person[0],1
                for j in range(1,len(prev_person)):
                    if prev_person[j] == num:
                        count +=1
                    else:
                        next_person += str(count)+num
                        num = prev_person[j]
                        count =1
                next_person += str(count)+num
                prev_person = next_person
            return prev_person
    

    解题思路:
    这个题就是题比较难读懂,我翻译了一下,其实就是:
    1.1
    2.描述的是1,是一个1,也就是11
    3.描述的是11,是两个1,也就是21

    1. 描述的是21,是一个2一个1,也就是12-11
    2. 描述的是1211, 是一个1,一个2,两个1,也就是11-12-21
    3. 描述的是111221,是三个1,两个2,一个1,也就是31-22-11
    4. 描述的是312211,是一个3一个1两个2两个1,也即是13-11-22-21
      以此类推
      所以对字符串进行一次遍历就好,将已生成的最后一个数称为‘上一个数’,再通过‘上一个数’推出‘下一个数’,然后把‘下一个数’的值赋给上一个数,继续遍历即可。

    No.39.组合总和

    难度:中等
    题目描述:


    在这里插入图片描述

    题解代码(Python3.8)

    class Solution:
        def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
            l=len(candidates)
            if l==0:
                return []
            candidates.sort()
            path=[]
            res=[]
            
            def backtrack(begin,path,target):
                if target==0:
                    res.append(path[:])
                for i in range(begin,l):
                    target_next=target-candidates[i]        #剪枝操作
                    if target_next<0:
                        break
                    path.append(candidates[i])
                    backtrack(i,path,target_next)
                    path.pop()
            
            backtrack(0,path,target)
            return res
    

    或许有用的知识点:
    这道题要用到回溯算法。
    a=p与a=p[:]的区别:a=p是把p的地址给a,p和a指向同一对象;而a=p[:]是创建一个内容与p全等的对象,命名为a,a指向p的副本,p和a指向不同对象。再回溯算法中,对可能被回溯的待保存元素,有时需要以a=p[:] 的方式储存。

    解题思路:
    定义回溯函数,在减的过程中,得到 0或者负数,就没有必要再走下去,所以这两种情况就分别表示成为叶子结点。此时递归结束,然后要发生回溯。注意,这道题中组合里的数字可以无限制重复被选取。而要想完成剪枝,前提是数组是有序的,因此我们需要对数组进行排序。注意,Python 中可变对象是引用传递,因此需要将当前 path 里的值拷贝出来,即a=p[:]。

    No.40.组合总和Ⅱ

    难度:中等
    题目描述:


    在这里插入图片描述

    题解代码(Python3.8)

    class Solution:
        def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
            l=len(candidates)
            if l==0:
                return []
            candidates.sort()                                           #初始化
            path=[]
            res=[]
            
            def backtrack(begin,path,target):
                if target==0:                                           #回溯出口
                    res.append(path[:])
                for i in range(begin,l):                                #回溯主体
                    target_next=target-candidates[i]    #剪枝操作
                    if target_next<0:
                        break
                    if i>begin and candidates[i-1]==candidates[i]:
                        continue
                    path.append(candidates[i])
                    backtrack(i+1,path,target_next)
                    path.pop()                                          #状态返回
            
            backtrack(0,path,target)
            return res
    

    或许有用的知识点:
    这道题要用到回溯算法。
    a=p与a=p[:]的区别:a=p是把p的地址给a,p和a指向同一对象;而a=p[:]是创建一个内容与p全等的对象,命名为a,a指向p的副本,p和a指向不同对象。再回溯算法中,对可能被回溯的待保存元素,有时需要以a=p[:] 的方式储存。

    解题思路:
    这道题跟‘39.组合总和’的思路类似,都是定义回溯函数,与其差别就是这道题组合中每个数只能用使用一次。在减的过程中,得到 0或者负数,就没有必要再走下去,所以这两种情况就分别表示成为叶子结点。此时递归结束,然后要发生回溯。要想完成剪枝,前提是数组是有序的,因此我们需要对数组进行排序。注意,Python 中可变对象是引用传递,因此需要将当前 path 里的值拷贝出来,即a=p[:]。

    相关文章

      网友评论

          本文标题:Python小白 Leetcode刷题历程 No.36-N

          本文链接:https://www.haomeiwen.com/subject/aazdxhtx.html