搜索题-记录

本文深入探讨了深度学习在计算机视觉领域的应用,包括图像分类、目标检测、语义分割等任务。通过分析二进制矩阵最短路径、单词接龙算法、岛屿数量计算以及省份数量统计等问题,展示了深度学习和搜索策略的有效性。此外,还介绍了如何解决被包围的区域、太平洋大西洋水流问题以及电话号码的字母组合。文章详细讨论了不同类型的回溯法和BFS策略,强调剪枝优化和时间复杂度分析的重要性。
摘要由CSDN通过智能技术生成

  • 注意双向BFS怎么写
  • 注意回溯的时间复杂度怎么计算
  • 注意回溯的各种剪枝:mark、避免相同元素重复、

2021/4/16

1091. 二进制矩阵中的最短路径

BFS 注意:

  • 遍历每一层完之后 ret+1;不是遍历每一个就ret+1
  • 每一层遍历的时候,只要节点被遍历过,就修改为1;就不会再append这个节点了
  • 不要设置 -1 直接设置1 不然超时,不知道为啥
class Solution:
    def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
        if(grid==None):
            return -1
        if(grid[0][0]==1):
            return -1
        n=len(grid)
        queue=[(0,0)]
        grid[0][0]=1
        ret=1
        while(queue):
            queue_len=len(queue) # 每一层的元素数量
            while(queue_len>0):
                # 取首元素
                cur=queue[0]
                if(cur[0]==n-1 and cur[1]==n-1):
                    return ret
                for direct in [(0,1),(0,-1),(1,-1),(1,0),(1,1),(-1,-1),(-1,0),(-1,1)]:
                    id1,id2=cur[0]+direct[0],cur[1]+direct[1]
                    # 判断是否出界
                    if(id1<0 or id1>=n or id2<0 or id2>=n):
                        continue
                    # 是否可达
                    if(grid[id1][id2]==0):
                        queue.append((id1,id2))
                        grid[id1][id2]=1
                        # # 到达右下角 这样写忽略一种情况 [[0]]
                        # if(id1==n-1 and id2==n-1):
                        #     return ret+1

                del queue[0]
                queue_len-=1
            ret+=1

        return -1
127. 单词接龙

单向bfs 从头到尾 太慢了 超出时间限制

class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        if(endWord not in wordList):
            return 0
        ret=1
        queue=[beginWord]
        mark=set() 
        mark.add(beginWord)# 已经可达的单词
        wordList=set(wordList)
        while(queue):
            # 对这一层的所有元素 遍历
            queue_len=len(queue)
            while(queue_len>0):
                s_cur=queue[0]
                # 搜寻所有
                for s in (wordList-mark):
                    if(self.isTrans(s_cur,s)):
                        if(s==endWord):
                            return ret+1
                        queue.append(s)
                        mark.add(s)
                del queue[0]
                queue_len-=1
            ret+=1
        return 0

    def isTrans(self,s1,s2):
        # 判断两个不同的字符能否 通过一个字符转化
        if(len(s1)!=len(s2)):
            return False
        s1_list=list(s1)
        s2_list=list(s2)
        if(s1_list==s2_list):
            return False
        for i in range(len(s1)):
            # 逐个去字符 判断是否相等
            ss1=s1_list.pop(i)
            ss2= s2_list.pop(i)
            # bug:replace默认替换等于该字符的第一个位置
            #if(s1.replace(s1[i],'', 1)==s2.replace(s2[i],'',1)):
            if(s1_list==s2_list):
                return True
            s1_list.insert(i,ss1)
            s2_list.insert(i,ss2)
        return False

别人的代码 单向bfs 可以成功 关键点:

  • ①mark wordlist 都用set 查找复杂度变为1
  • ②直接对于当前出队列的元素 修改每个位置 上 每个元素 看修改之后在不在 wordlist里;而不是遍历wordlist 再和当前元素判断

② 对于一个元素来说;这个代码时间复杂度 1×26×len(cur_word);自己的是 len(word_list)×len(cur_word);很明显当word_list很大的时候,自己这种写法就慢了,导致超出时间限制。

class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        st=set(wordList)
        if endWord not in st:
            return 0
        m=len(beginWord)    
    
        queue=collections.deque()        
        queue.append(beginWord)

        visited=set()
        visited.add(beginWord)
        step=0

        while queue:
            step+=1
            for k in range(len(queue)):
                cur=queue.popleft()
                if cur==endWord:
                    return step
                
                for i in range(m):                
                    for j in range(26):
                        tmp=cur[:i]+chr(97+j)+cur[i+1:]
                        if tmp not in visited and tmp in st:
                            queue.append(tmp)
                            visited.add(tmp)
                       
        return 0

官方:双向BFS,主要是构造两个队列,每次以短的遍历;当一阶可达word in 右队列时,终止

class Solution:
    import collections

    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        wordList=set(wordList)
        if endWord not in wordList:
            return 0
        m=len(beginWord)    
    
        queueLeft=collections.deque()   
        queueRight=collections.deque()

        queueLeft.append(beginWord) # 短队列
        queueRight.append(endWord) # 长队列

        visited=set([beginWord,endWord]) # 共用visited
        step=0

        while queueLeft:
            step+=1
            # 对每一层的元素遍历
            for k in range(len(queueLeft)):
                cur=queueLeft.popleft()
                # 判断当前元素所能可达的元素 并append到左队列
                for i in range(len(cur)):
                    for j in range(26):
                        tmp=cur[:i]+chr(97+j)+cur[i+1:] # 构造new word
                        if(tmp in queueRight):
                            return step+1
                        if(tmp not in visited and tmp in wordList):
                            queueLeft.append(tmp)
                            visited.add(tmp)
            #更新左队列 右队列 短的为左
            if(len(queueRight)<len(queueLeft)):
                queueLeft,queueRight=queueRight,queueLeft

        return 0

2021/4/24

695. 岛屿的最大面积

DFS

class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
        self.row=len(grid)
        self.col=len(grid[0])
        maxArea=0
        for i in range(self.row):
            for j in range(self.col):
                maxArea=max(maxArea,self.dfs(grid,i,j))
                
        return maxArea

    # 从 [i,j] 点出发的最大area
    def dfs(self,grid,i,j):
        # 超出当前grid
        if(i>=self.row or i<0 or j>=self.col or j<0):
            return 0
        if(grid[i][j]==0):
            return 0
        else:
            area=1
            grid[i][j]=0 #标记为visited
            for direct in ([1,0],[-1,0],[0,1],[0,-1]):
                area+=self.dfs(grid,i+direct[0],j+direct[1])
        return area

DFS+栈

class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
        row=len(grid)
        col=len(grid[0])
        maxArea=0
        for i in range(row):
            for j in range(col):
                if(grid[i][j]==0):
                    continue
                # 只有遇到1 再用栈
                stack=[[i,j]]
                grid[i][j]=0
                area=0
                while(stack):
                    area+=1
                    cur=stack.pop() # 出栈
                    for direct in ([1,0],[-1,0],[0,1],[0,-1]):
                        left_idx=cur[0]+direct[0]
                        right_idx=cur[1]+direct[1]
                        if(left_idx>=0 and left_idx<row and right_idx>=0 and right_idx<col and grid[left_idx][right_idx]==1):
                            stack.append([left_idx,right_idx])
                            grid[left_idx][right_idx]=0 # 注意一定入栈之后就mark,如果出栈再mark,结果可能会增大,因为可能被同一个栈的元素visit多次

                maxArea=max(maxArea,area)
                
        return maxArea

2021/4/25

200. 岛屿数量

按照area面积来判断的,官方给的是将遍历过的1标记为2.

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        num=0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if(self.dfs(grid,i,j)>0):
                    num+=1
        return num

    # 返回[i,j] 所在岛屿面积            
    def dfs(self,grid,i,j):
        row=len(grid)
        col=len(grid[0])
        if(i<0 or i>=row or j<0 or j>=col or grid[i][j]=='0'):
            return 0
        else:
            area=1
            grid[i][j]='0' # mark 1 为 0
            for direct in ([0,1],[0,-1],[-1,0],[1,0]):
                area+=self.dfs(grid,i+direct[0],j+direct[1])
            return area
547. 省份数量

从每个城市出发,去mark所有相连的城市,标记为2:

  • 注意当 dp[i][j]为1时 且 i!=j时,就将dp[j][j] 也mark
  • 最终看对角线元素 有几个1即可
class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n=len(isConnected)
        num=0
        for i in range(n):
            if(isConnected[i][i]==1):
                num+=1
                self.dfs(isConnected,i)
        return num

    # 从 城市i出发 mark所有相连的
    def dfs(self,grid,i):
        n=len(grid)
        for j in range(n):
            if(grid[i][j]==1 and j!=i):
                grid[i][j]=2
                grid[j][j]=2
                self.dfs(grid,j)

使用一个set来标记所有visit的城市 能更快一点

class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n=len(isConnected)
        num=0
        visit=set()

        # 从城市i出发 去mark所有相连的
        def dfs(i):
            for j in range(n):
                if(isConnected[i][j]==1 and j not in visit):
                    visit.add(j)
                    dfs(j)

        for i in range(n):
            if(isConnected[i][i]==1 and i not in visit):
                visit.add(i)
                dfs(i)
                num+=1

        return num
130. 被围绕的区域

自己写的意思对了,就是太繁琐了。。

class Solution:
    def solve(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        self.row=len(board)
        self.col=len(board[0])
        if(self.row<=1 or self.col<=1):
            return 0
        # 先遍历 四个边 mark为 '1'
        for i in range(self.col):
            self.dfs(board,0,i,'O','1')
        for i in range(self.col):
            self.dfs(board,self.row-1,i,'O','1')
        for i in range(self.row):
            self.dfs(board,i,0,'O','1')
        for i in range(self.row):
            self.dfs(board,i,self.col-1,'O','1')
        # print(board)
        # 在遍历所有 将 '0' mark为'X'
        for i in range(self.row):
            for j in range(self.col):
                self.dfs(board,i,j,'O','X')
        # print(board)
        # 最后将'1' mark为 '0'
        for i in range(self.row):
            for j in range(self.col):
                self.dfs(board,i,j,'1','O')
        # print(board)

    def dfs(self,grid,i,j,orgin,new):
        if(i>=0 and i<self.row and j>=0 and j<self.col and grid[i][j]==orgin):
            grid[i][j]=new
            for direct in ([0,1],[0,-1],[-1,0],[1,0]):
                self.dfs(grid,i+direct[0],j+direct[1],orgin,new)

看了看官方的,其实最主要的区别是一个,自己写冗余了:

  • 1,先遍历四个边,标记O为T
  • 2,遍历整个grid
    • 如果是T,转为O
    • 如果是O,说明不和边相连,那么它肯定就会被X替换 (自己又额外判断了这个)

2021/4/30

417. 太平洋大西洋水流问题

逆流而上的思路,从四个边往上拓展

自己刚开始这个内存太大。。 用两个set存储分别的点

class Solution:
    def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
        self.row=len(heights)
        self.col=len(heights[0])
        left=set()
        right=set()

        # 从太平洋的初始边开始遍历
        for j in range(self.col):
            left.add((0,j))
            self.dfs(heights,0,j,left)
        
        for i in range(self.row):
            left.add((i,0))
            self.dfs(heights,i,0,left)

        # 从大西洋的初始边开始遍历
        for j in range(self.col):
            left.add([self.row-1,j])
            self.dfs(heights,self.row-1,j,right)
        
        for i in range(self.row):
            left.add([i,self.col-1])
            self.dfs(heights,i,self.col-1,right)

        return list(left&right)
        

    # 从[i,j]开始流动
    def dfs(self,grid,i,j,curSet):
        for direct in ([0,1],[0,-1],[1,0],[-1,0]):
            idx1=i+direct[0]
            idx2=j+direct[1]
            if(idx1<0 or idx1>=self.row or idx2<0 or idx2>=self.col):
                continue
            if(grid[idx1][idx2]>=grid[i][j]):
                curSet.add((idx1,idx2))
                self.dfs(grid,idx1,idx2,curSet)

看了一个DFS的题解,用两个数组 0/1 来标记能否到达 left和right

class Solution:
    def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
        self.row=len(heights)
        self.col=len(heights[0])
        left=[[0]*self.col for _ in range(self.row)]
        right=[[0]*self.col for _ in range(self.row)]

        # 从太平洋的初始边开始遍历
        for j in range(self.col):
            left[0][j]=1
            self.dfs(heights,0,j,left)
        
        for i in range(self.row):
            left[i][0]=1
            self.dfs(heights,i,0,left)

        # 从大西洋的初始边开始遍历
        for j in range(self.col):
            right[self.row-1][j]=1
            self.dfs(heights,self.row-1,j,right)
        
        for i in range(self.row):
            right[i][self.col-1]=1
            self.dfs(heights,i,self.col-1,right)

        return [[i,j] for i in range(self.row) for j in range(self.col) if left[i][j] and right[i][j]]
        

    # 从[i,j]开始流动
    def dfs(self,grid,i,j,visit):
        for direct in ([0,1],[0,-1],[1,0],[-1,0]):
            idx1=i+direct[0]
            idx2=j+direct[1]
            if(idx1<0 or idx1>=self.row or idx2<0 or idx2>=self.col):
                continue
            if(grid[idx1][idx2]>=grid[i][j] and visit[idx1][idx2]==0):
                visit[idx1][idx2]=1
                self.dfs(grid,idx1,idx2,visit)
17. 电话号码的字母组合

回溯法 2-3-4

  • 顺序遍历,按了这个元素 就不会用到了
  • combine是一个组成的字符串,可能是 ‘’,a,ad,adg,只有当长度=num数量时,才会加入到result中
  • i是表明当前按的元素的idx
class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        nums=list(digits)
        if(len(nums)==0):
            return []
        numDict={
            '2':['a','b','c'],
            '3':['d','e','f'],
            '4':['g','h','i'],
            '5':['j','k','l'],
            '6':['m','n','o'],
            '7':['p','q','r','s'],
            '8':['t','u','v'],
            '9':['w','x','y','z']
        }
        result=[]
        # 按了这个元素 就不会用到了
        # combine是一个组成的字符串 i 表示从idx==i的数字开始
        def backTrack(combine,i):
            if(i==len(digits)):
                result.append(combine)
            else:
                for letter in numDict[nums[i]]:
                    backTrack(combine+letter,i+1)

        backTrack('',0)
        return result

2021/5/3

93. 复原 IP 地址

自已写的第一版 还可以剪枝优化

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        if(len(s)<4 or len(s)>12):
            return []
        result=[]

        def trackBack(cur,i):
            if(i==len(s)):
                result.append(cur[:-1])
            else:
                for j in range(1,4):
                    if(i+j<=len(s) and int(s[i:i+j])>=0 and int(s[i:i+j])<=255):
                        trackBack(cur+str(int(s[i:i+j]))+'.',i+j)
            
        trackBack('',0)         
        ips=[]
        for ip in result:
            ipList=ip.split('.')
            if(len(ipList)==4):
                curLen=0
                for ss in ipList:
                    curLen+=len(ss)
                if(curLen==len(s)):
                    ips.append(ip)

        return ips

简化了一下代码 但仍然很慢。。

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        if(len(s)<4 or len(s)>12):
            return []
        result=[]

        def trackBack(cur,i):
            if(i==len(s) and len(cur.split('.'))==5 and len(cur)==len(s)+4):
                result.append(cur[:-1])
            else:
                for j in range(1,4):
                    if(i+j<=len(s) and len(cur.split('.'))<5): # 之前写的<4错误,因为.后面的''会占一个元素
                        new=int(s[i:i+j])
                        # 保证数字0-255
                        if(new>0 and new<=255):
                            trackBack(cur+str(new)+'.',i+j)
                        # 避免 '00' 出现
                        if(new==0 and j==1):
                            trackBack(cur+str(new)+'.',i+j)
                            
        trackBack('',0)         

        return result
79. 单词搜索

79. 单词搜索 这道题着实卡了很久
一直写的是注释里的函数,发现错误的原因竟然是要额外判断board[i][j]==word[0],不让的话,第一个=word[0]的元素就无法回溯了。。。。。。。!!!!

  • 一直想写一个函数就完事了,但是考虑第一个上述的元素的回溯!!
  • 注意这里:下一个元素不是word路径上,不代表当前元素不在正确路径上
class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        m=len(board)
        n=len(board[0])
        if(len(word)>m*n):
            return False
        mark=[[0]*n for _ in range(m)]  

        # 从[i,j]判断是不是==word[idx],且位于word路径上
        def backTrack(i,j,idx,mark):
            if(board[i][j]!=word[idx]):
                return False
            if(idx==len(word)-1):
                return True

            #print(i,j,idx)
            # [i,j]等于idx对应char,就mark[i,j]
            mark[i][j]=1
            for direct in [(0,1),(0,-1),(1,0),(-1,0)]:
                i1=i+direct[0]
                j1=j+direct[1]
                # 未超界
                if(i1>=0 and i1<m and j1>=0 and j1<n and mark[i1][j1]==0):
                    mark[i1][j1]=1
                    # 如果下一个元素是word路径上的 
                    if(backTrack(i1,j1,idx+1,mark)):
                        return True
                    # 如果下一个元素不是word路径上的,那么回溯,即mark下一个元素为0 
                    else:
                        mark[i1][j1]=0
                        # 注意这里!!:下一个元素不是word路径上,不代表当前元素不在正确路径上
                        # return False

        for i in range(m):
            for j in range(n):
                if(backTrack(i,j,0,mark)):
                    return True
                else:
                    mark[i][j]=0

        return False


# class Solution:
#     def exist(self, board: List[List[str]], word: str) -> bool:
#         m=len(board)
#         n=len(board[0])
#         if(len(word)>m*n):
#             return False
#         mark=[[0]*n for _ in range(m)]  

#         # 从[i,j]判断是不是==word[idx],且位于word路径上
#         def backTrack(i,j,idx,mark):
#             if(board[i][j]!=word[idx]):
#                 return False
#             if(idx==len(word)-1):
#                 return True

#             print(i,j,idx)
#             # [i,j]等于idx对应char,就mark[i,j]
#             mark[i][j]=1
#             for direct in [(0,1),(0,-1),(1,0),(-1,0)]:
#                 i1=i+direct[0]
#                 j1=j+direct[1]
#                 # 未超界
#                 if(i1>=0 and i1<m and j1>=0 and j1<n and mark[i1][j1]==0):
#                     mark[i1][j1]=1
#                     # 如果下一个元素是word路径上的 
#                     if(backTrack(i1,j1,idx+1,mark)):
#                         return True
#                     # 如果下一个元素不是word路径上的,那么回溯,即mark下一个元素为0 
#                     else:
#                         mark[i1][j1]=0
#                         # 注意这里!!:下一个元素不是word路径上,不代表当前元素不在正确路径上
#                         # return False

#         for i in range(m):
#             for j in range(n):
#                 if(backTrack(i,j,0,mark)):
#                     return True

#         return False
        
257. 二叉树的所有路径
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        if(root==None):
            return []
        result=[]
        def backTrack(curPath,curNode):
            # 遇到叶子节点了
            if(curNode.left==None and curNode.right==None):
                curPath=curPath+'->'+str(curNode.val)
                result.append(curPath[2:])
            if(curNode.left):
                backTrack(curPath+'->'+str(curNode.val),curNode.left)
            if(curNode.right):
                backTrack(curPath+'->'+str(curNode.val),curNode.right)
                
        backTrack('',root)
        return result

46. 全排列
  • ①自己写的,有点固定思路了,只会写这种str,split,join这种办法。。
  • ②修改为list形式的,注意用cur+[]传参,别直接在cur上append
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        if(len(nums)<1):
            return []
        result=[]
        mark=[0]*len(nums)

        # 当前组合str;当前用了多少Num
        def combine(cur,numCount,mark):
            if(numCount==len(nums)):
                result.append([int(tmp) for tmp in cur[1:].split('.')])
            else:
                # 从当前未mark的nums中选一个 遍历所有可能
                for idx in range(len(mark)):
                    if(mark[idx]==0):      
                        mark[idx]=1
                        combine(cur+'.'+str(nums[idx]),numCount+1,mark)
                        # 遍历一条depth==numlen的路径之后 回溯为0
                        mark[idx]=0

        combine('',0,mark)
        return result
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        if(len(nums)<1):
            return []
        result=[]
        mark=[0]*len(nums)

        def combine(cur,idx):
            if(idx==len(nums)):
                result.append(cur)
            for i in range(len(mark)):
                if(mark[i]==0):
                    mark[i]=1
                    #cur.append(nums[i]) #注意别用这个,就直接修改掉cur的值了。。。
                    combine(cur+[nums[i]],idx+1)
                    mark[i]=0

        combine([],0)

        return result

自己写的多用了标记数组,官方去掉了标记数组,将题目给定的 nn 个数的数组 \textit{nums}nums 划分成左右两个部分,左边的表示已经填过的数,右边表示待填的数,我们在回溯的时候只要动态维护这个数组即可。

  • 注意nums[:] 是返回局部值,nums返回原对象
  • 嵌套函数修改不了外部函数的参数
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        if(len(nums)<1):
            return []
        result=[]

        # diff是区分[用了的|没用的] nums[diff]是未用过的首元素
        def combine(diff=0):
            # 未用过元素的idx超出了len-1,说明用完所有元素了
            if(diff==len(nums)):
                #result.append(nums[]) # 注意这个!!!nums[:] 是返回值,nums返回原值
                result.append(nums[:])
            # 所有下一个可用的集合中选 遍历
            for i in range(diff,len(nums)):
                nums[diff],nums[i]=nums[i],nums[diff]
                combine(diff+1)
                # 当一条path完成之后 回溯 
                nums[i],nums[diff]=nums[diff],nums[i]

        combine()

        return result

2021/5/4

47. 全排列 II

个人真的不理解这道题。。

  • 关键是如何保证idx位置重复的num不会填进去;然后在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。但这个mark[i-1]==0着实不理解,如果前一个未访问,怎么跳过呢??
class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        if(len(nums)<1):
            return []
        result=[]
        mark=[0]*len(nums)

        def backTrack(cur,idx,mark):
            if(idx==len(nums)):
                result.append(cur)
            for i in range(len(nums)):
                # # 使用nums[i]的条件1:必须没有mark过
                # if(mark[i]==0):
                #     # 条件2:相同的元素没有被mark过 (因为本题中对nums进行了排序 所以和上一个比即可)
                #     if(i==0 or (i>0 and nums[i]!=nums[i-1] and mark[i-1]==1)):
                #         mark[i]=1
                #         backTrack(cur+[nums[i]],idx+1,mark)
                #         # 1 回溯当前元素用没用过
                #         mark[i]=0
                
                if(mark[i]==1):
                    continue
                # Q:为什么要加上mark[i-1]==0!!!??
                # A:为了保证当[x1,x2,x3]是逐个填入的,即x2必须在x1填入之后填入
                if(i>0 and nums[i]==nums[i-1] and mark[i-1]==0):
                    continue
                mark[i]=1
                backTrack(cur+[nums[i]],idx+1,mark)
                mark[i]=0

        nums.sort()
        backTrack([],0,mark)
        return result
77. 组合

自己先写的一版,关键是避免重复即可,但是时间较慢。

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        if(k>n or n<1):
            return []
        result=[]
        # 从 1,2,..,n 中选k个数
        mark=[0]*n
        # idx是k的index
        def combine(cur,idx,mark):
            if(idx==k):
                result.append(cur)
            # 在未选择的数中选择 且为了避免重复 保证新增的大于上一个(上一个即目前最大的)
            for i in range(n):
                if(mark[i]==0):
                    if(len(cur)==0 or (i>0 and i+1>cur[-1])):
                        mark[i]=1
                        combine(cur+[i+1],idx+1,mark)
                        mark[i]=0

        combine([],0,mark)
        return result

不需要额外mark数组

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        if(k>n or n<1):
            return []
        result=[]

        # 保证填入的比上一个填入的大即可
        def combine(cur,idx):
            if(idx==k):
                result.append(cur)
            # 1 cur还是空 随便填
            if(idx==0):
                for i in range(n):
                    combine(cur+[i+1],idx+1)
            # 2 cur不是空 大于最后一个即可
            else:
                for i in range(cur[-1],n):
                    combine(cur+[i+1],idx+1)

        combine([],0)
        return result
39. 组合总和
  • 关键避免重复组合即可
  • 不知道怎么剪枝时,可以画树图来观察,发现只要进行排序,保证每次新的Num>=当前最大的就ok了
class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        if(len(candidates)<1 or target<1):
            return []
        result=[]

        def dfs(cur,curSum):
            if(curSum==target):
                result.append(cur)
            # 避免重复组合 sort 候选List,保证新的>=上一个即可
            for num in candidates:
                if(curSum==0 or(curSum>0 and num>=cur[-1]) and num<=target and num+curSum<=target):
                    dfs(cur+[num],curSum+num)

        candidates.sort()
        dfs([],0)
        return result
40. 组合总和 II

关键是重复有两种情况

  • 1,和39题一样,[1,2,3] [2,3,1]\,[3,2,1] 这种重复;候选list排序,保证新的>=cur[-1]即可
  • 2,[1,1,2,5] 中[1,2]\,[1,2] 这种重复,和47题一样,这会理解那个nums[i] ==nums[i-1] 时,必须确保nums[\i-1]为mark的理由了。。
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        if(len(candidates)<1 or target<1):
            return []
        result=[]
        mark=[0]*len(candidates)
        def backTrack(cur,curSum,mark):
            if(curSum==target):
                result.append(cur)
            for i in range(0,len(candidates)):
                # 保证新增的未mark
                if(mark[i]==1):
                    continue
                # 保证新增的大于等于当前最大值 (避免重复1)
                if(curSum>0 and candidates[i]<cur[-1]):
                    continue
                # 保证curSum时nums[i]不被重复搜索(避免重复2);
                # 即nums[i]==nums[i-1]时 必须保证nums[i-1]被mark
                if(i>0 and candidates[i]==candidates[i-1] and mark[i-1]==0):
                    continue
                # 保证新增之后sum <= target
                if(curSum+candidates[i]>target):
                    continue
                mark[i]=1
                backTrack(cur+[candidates[i]],curSum+candidates[i],mark)
                mark[i]=0 # 回溯

        candidates.sort()
        backTrack([],0,mark)
        return result

但是自己的有点费时间,又看了了下某题解,可以不需要mark数组:

  • 但自己还是没写出来重复那里的剪枝:
  • 其实目的都是即nums[i]==nums[i-1]时 必须保证nums[i-1]被append进去了
  • 因为一次只append一个元素 所以如果在i>begin的情况下nums[i]==nums[i-1]了,那么直接跳过;
  • 自己写的虽然逻辑上好像正确,但是有种情况会出错:出现多个[2,2,2,2,2,]重复值时,会append多个2进去,但实际append一个2才正确

正确的是:

if(i>begin and candidates[i]==candidates[i-1]):

自己写的是:

                if(i>begin and candidates[i]==candidates[i-1]):
                    if(curSum>0 and cur[-1]!=candidates[i-1]):
                        continue
                    elif(curSum==0):
                        continue
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        if(len(candidates)<1 or target<1):
            return []
        result=[]
        # 由于从小到大排 所以不需要mark数组 只需要一个begin下标即可 
        #mark=[0]*len(candidates)

        def backTrack(cur,curSum,begin):
            if(curSum==target):
                result.append(cur)
            for i in range(begin,len(candidates)):
                # 保证curSum时nums[i]不被重复搜索;
                # 即nums[i]==nums[i-1]时 必须保证nums[i-1]被append进去了
                # 因为一次只append一个元素 所以如果在i>begin的情况下nums[i]==nums[i-1]了,那么直接跳过
                if(i>begin and candidates[i]==candidates[i-1]):
                    continue
                    # if(curSum>0 and cur[-1]!=candidates[i-1]):
                    #     continue
                    # elif(curSum==0):
                    #     continue
                # 保证新增之后sum <= target
                if(curSum+candidates[i]>target):
                    continue
                backTrack(cur+[candidates[i]],curSum+candidates[i],i+1)

        candidates.sort()
        backTrack([],0,0)
        return result
216. 组合总和 III

感觉还是40难一点

class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        if(k<1 or n<1):
            return []
        result=[]
        # begin是未选择的数的第一个
        def backTrack(cur,curSum,begin):
            if(curSum==n and len(cur)==k):
                result.append(cur)
            # 数量保证<=k 正整数<=9
            if(len(cur)>k or begin>9):
                return 
            for num in range(begin,10):
                if(num+curSum<=n):
                    backTrack(cur+[num],curSum+num,num+1)

        backTrack([],0,1)
        return result
78. 子集

第一版,就是最简单的从子集长度1,2,3…len(nums)一直做

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        if(len(nums)<1):
            return [[]]
        result=[[]]
        # 子集的长度 1,2,...len(nums)
        # begin是未选择的num的第一个index
        def dfs(cur,length,begin):
            if(len(cur)==length):
                result.append(cur)
            # 遍历完nums了
            if(begin>=len(nums)):
                return
            for idx in range(begin,len(nums)):
                dfs(cur+[nums[idx]],length,idx+1)

        for k in range(1,len(nums)+1):
            dfs([],k,0)

        return result

但是可以看出性质,假如长度为5,那么长度=1和长度=4的子集是互补的;但是注意一种情况:nums长度为6,长度=3的子集不需要append互补子集,因为其互补子集还是自己长度=3的子集

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        if(len(nums)<1):
            return [[]]
        result=[]
        # 子集的长度 1,2,...len(nums)
        # begin是未选择的num的第一个index
        def dfs(cur,length,begin):
            if(len(cur)==length):
                result.append(cur) # {长度=length}的子集
                # 长度偶数时,一半length时不需要考虑互补子集
                if(len(nums)%2==0 and length==len(nums)//2):
                    return
                result.append(list(set(nums)-set(cur))) # 互补的{长度=len(nums)-length}的子集
            # 遍历完nums了
            if(begin>=len(nums)):
                return
            for idx in range(begin,len(nums)):
                dfs(cur+[nums[idx]],length,idx+1)

        #nums.sort()
        for k in range(0,len(nums)//2+1):
            dfs([],k,0)

        return result
90. 子集 II

有重复子集的情况,需要对nums排序,然后排除重复的情况(剪枝)

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        result=[]
        # begin为未选择的num的第一个idx
        def trackBack(cur,length,begin):
            if(len(cur)==length):
                result.append(cur)
                # # 互补子集
                # if(len(nums)%2==0 and length==len(nums)/2):
                #     return
                # result.append(list(set(nums)-set(cur)))
            # 遍历完nums了
            if(begin>=len(nums)):
                return
            # 保证不含重复的子集:对nums排序 当nums[i]==nums[i-1]时 保证nums[i-1]必须被append进去
            for idx in range(begin,len(nums)):
                # 因为一次append一个元素,肯定要append第一个相等的元素
                if(idx>begin and nums[idx]==nums[idx-1]):
                    continue
                trackBack(cur+[nums[idx]],length,idx+1)

        nums.sort()
        # 5:0,1,2
        for k in range(0,len(nums)+1):
            trackBack([],k,0)

        return result

2021/5/6

131. 分割回文串

自己的答案太耗时了

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        if(len(s)<1):
            return []
        result=[]
        # 判断s1是否为回文
        def isReverseStr(s1):
            idx1=0
            idx2=len(s1)-1
            while(idx1<idx2):
                if(s1[idx1]!=s1[idx2]):
                    return False
                else:
                    idx1+=1
                    idx2-=1
            return True

        # begin是未分割的字符中第一个idx
        def dfs(cur,begin):
            # 分割完s了
            if(begin==len(s)):
                result.append(cur)
            # 进行分割 从可能长度1,2,...len-begin中选择
            for i in range(1,len(s)-begin+1):
                #print(isReverseStr(s[begin:begin+i]))
                if(isReverseStr(s[begin:begin+i])):
                    # print(s[begin:begin+i])
                    dfs(cur+[s[begin:begin+i]],begin+i)
        
        dfs([],0)

        return result

是因为双指针判断回文字符会出现多次冗余,先dp找出所有的回文字符,再dfs,关键有:

  • 怎么写dp,dp[i][j]==(s[i]==\s[j]) and dp[i+1][j-1],默认初始化都True,所以不用担心s[4][5]:①s[4]和s[5]相等判断即可 ②s[5][4]默认True
  • dfs的时候注意下标,dp[i][j]表示的是s[i:j+1]是回文
class Solution:
    def partition(self, s: str) -> List[List[str]]:
        if(len(s)<1):
            return []
        result=[]
        
        # 预处理 dp[i][j]表示 s[i:j+1] 是否为回文串
        dp=[[True]*len(s) for _ in range(len(s))]
        # 只有右上三角有意义 dp[i][j] 只和dp[i+1][j-1]有关 倒着遍历
        for i in range(len(s)-1,-1,-1):
            for j in range(i+1,len(s)):
                dp[i][j]=(s[i]==s[j]) and dp[i+1][j-1]

        # begin是未分割的字符中第一个idx
        def dfs(cur,begin):
            # 分割完s了
            if(begin==len(s)):
                result.append(cur)
            # dp[begin][begin]....dp[begin][len(s)-1]
            for i in range(begin,len(s)):
                if(dp[begin][i]):
                    dfs(cur+[s[begin:i+1]],i+1)
        
        dfs([],0)

2021/5/7

37. 解数独

这题自以为没问题,但是写出来之后各种小问题。。

  • 注意一旦flag=True 就都要return
  • 什么时候需要 idxSet.append((i,j)) ,当choose中进行dfs之后没有一条路能让flag变为true,就需要append回溯;这几条路之间不需要append
class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        from collections import defaultdict
        rowHash=defaultdict(set)
        colHash=defaultdict(set)
        blockHash=defaultdict(set)
        allNum={'1','2','3','4','5','6','7','8','9'}
        numCount=0
        idxSet=[]

        def getBlockIdx(i,j):
            return int(i/3)*3+int(j/3)

        # 初始化3类哈希表
        for i in range(9):
            for j in range(9):
                if(board[i][j]!='.'):
                    numCount+=1
                    rowHash[i].add(board[i][j])
                    colHash[j].add(board[i][j])
                    blockHash[getBlockIdx(i,j)].add(board[i][j])
                else:
                    idxSet.append((i,j))

        # # dfs 当前idx位置可选的值集合是choose
        # def dfs(idxSet,board):
        #     print(len(idxSet))
        #     if(len(idxSet)==0):
        #         return True
        #     i,j=idxSet.pop()
        #     curChoose=list(allNum-(rowHash[i]|colHash[j]|blockHash[getBlockIdx(i,j)]))[:]
            
        #     for num in curChoose:
        #         board[i][j]=num
        #         rowHash[i].add(num)
        #         colHash[j].add(num)
        #         blockHash[getBlockIdx(i,j)].add(num)
        #         if(dfs(idxSet,board)):
        #             return True
        #         else:
        #             # 回溯
        #             board[i][j]='.'
        #             rowHash[i].remove(num)
        #             colHash[j].remove(num)
        #             blockHash[getBlockIdx(i,j)].remove(num)
        #             idxSet.append((i,j))

        #     return False

        # dfs
        self.flag=False
        def dfs(idxSet,board):
            # if(len(idxSet)<10):
            #     print(len(idxSet))
            if(len(idxSet)==0):
                self.flag=True
                print(board)
                return 
            


            i,j=idxSet.pop()
            curChoose=list(allNum-(rowHash[i]|colHash[j]|blockHash[getBlockIdx(i,j)]))[:]
            # print(curChoose)

            curFlag=False
            for num in curChoose:
                board[i][j]=num
                rowHash[i].add(num)
                colHash[j].add(num)
                blockHash[getBlockIdx(i,j)].add(num)
                # dfs
                dfs(idxSet,board)
                # 回溯
                if(not self.flag):
                    board[i][j]='.'
                    rowHash[i].remove(num)
                    colHash[j].remove(num)
                    blockHash[getBlockIdx(i,j)].remove(num)
                    #idxSet.append((i,j))
                else:
                    curFlag=True
                    return # 必须加这个return 不然board正确的会被修改
                
            if(not curFlag):
                idxSet.append((i,j))

                
        dfs(idxSet,board)

稍微精简一下

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        from collections import defaultdict
        rowHash=defaultdict(set)
        colHash=defaultdict(set)
        blockHash=defaultdict(set)
        allNum={'1','2','3','4','5','6','7','8','9'}
        numCount=0
        idxSet=[]

        def getBlockIdx(i,j):
            return int(i/3)*3+int(j/3)

        # 初始化3类哈希表
        for i in range(9):
            for j in range(9):
                if(board[i][j]!='.'):
                    numCount+=1
                    rowHash[i].add(board[i][j])
                    colHash[j].add(board[i][j])
                    blockHash[getBlockIdx(i,j)].add(board[i][j])
                else:
                    idxSet.append((i,j))

        # dfs
        self.flag=False
        def dfs(idxSet):
            if(len(idxSet)==0):
                self.flag=True
                #print(board)
                return 

            # # 出现无可选情况时 就return 
            # for idx in idxSet:
            #     i,j=idx
            #     if(len(allNum-(rowHash[i]|colHash[j]|blockHash[getBlockIdx(i,j)]))==0):
            #         return 

            i,j=idxSet.pop()
            curChoose=list(allNum-(rowHash[i]|colHash[j]|blockHash[getBlockIdx(i,j)]))[:]

            curFlag=False
            for num in curChoose:
                board[i][j]=num
                rowHash[i].add(num)
                colHash[j].add(num)
                blockHash[getBlockIdx(i,j)].add(num)
                # dfs
                dfs(idxSet)
                # 回溯
                if(not self.flag):
                    board[i][j]='.'
                    rowHash[i].remove(num)
                    colHash[j].remove(num)
                    blockHash[getBlockIdx(i,j)].remove(num)
                    #idxSet.append((i,j))
                else:
                    curFlag=True
                    return # 必须加这个return 不然board第一次是正确,后面又会被修改掉
                
            if(not curFlag):
                idxSet.append((i,j))

        dfs(idxSet)
51. N 皇后

这题明明比上一题简单。。
关键就是判断每个Q的可选列位置:

  • 排除掉同一列
  • 排除掉同一斜线(考虑两种斜线,右斜和左斜)
class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        # Q1:[i1,j] 皇后1 只能在第一行,列可选
        result=[]

        def dfs(Q,idx):
            if(idx==n):
                result.append(['.'*q+'Q'+'.'*(n-q-1) for q in Q])
            colHave=set()
            for i in range(len(Q)):
                # 同一列
                colHave.add(Q[i])
                # 斜线
                if(0<=Q[i]+idx-i<n):
                    colHave.add(Q[i]+idx-i)
                if(0<=Q[i]-idx+i<n):
                    colHave.add(Q[i]-idx+i)

            for j in set(range(n))-set(colHave):
                dfs(Q+[j],idx+1)

        dfs([],0)

        return result
基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip 个人大四的毕业设计、课程设计、作业、经导师指导并认可通过的高分设计项目,评审平均分达96.5分。主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。 [资源说明] 不懂运行,下载完可以私聊问,可远程教学 该资源内项目源码是个人的毕设或者课设、作业,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96.5分,放心下载使用! 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),供学习参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值