• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
fangleSea
博客园    首页    新随笔    联系   管理    订阅  订阅
代码随想录|回溯算法(终回)
 * 491.递增子序列

* 46.全排列

* 47.全排列 II

332.重新安排行程 

51. N皇后

37. 解数独 

总结 

491.递增子序列

注意使用set来判断这一层是否有用过相同的数字,因为这题的数不是order的,所以不能和前者相对比

在python中的set使用add来增加

class Solution:
    def findSubsequences(self, nums: List[int]) -> List[List[int]]:
        result = []
        ans_now = []
        self.combination(nums, result, ans_now, 0)
        return result
    
    def combination(self, nums, result, ans_now, index):
        if (len(ans_now) >= 2):
            result.append(ans_now[:])
        used = set()
        for i in range(index, len(nums)):
            if(index > 0 and nums[index-1] > nums[i]):
                continue
            if(nums[i] in used):
                continue
            used.add(nums[i])
            ans_now.append(nums[i])
            self.combination(nums, result, ans_now, i+1)
            ans_now.pop()
        return 

46.全排列

class Solution:
    # def backtrack(self, use_list, num_left, now_list):
    #     if num_left == 0:
    #         self.answer.append(now_list)
    #         return 
    #     for i in range(self.n):
    #         if use_list[i]:
    #             use_list[i] = False
    #             now_list.append(self.nums[i])
    #             self.backtrack(use_list, num_left-1, now_list[:])
    #             now_list.pop()
    #             use_list[i] = True
            
    
    # def permute(self, nums: List[int]) -> List[List[int]]:
    #     self.nums = nums
    #     self.answer = []
    #     self.n = len(nums)
    #     use = [True for _ in range(self.n)]
    #     self.backtrack(use, self.n, [])
    #     return self.answer


    def permute(self, nums: List[int]) -> List[List[int]]:
        result = []
        now_ans = []
        use = [True for _ in range(len(nums))]
        self.backtrack(use, result, now_ans, nums)
        return result
    
    def backtrack(self, use, result, now_ans, nums):
        if len(now_ans) == len(nums):
            result.append(now_ans[:])
            return 
        for i in range(len(nums)):
            if (use[i]):
                now_ans.append(nums[i])
                use[i] = False
                self.backtrack(use, result, now_ans, nums)
                use[i] = True
                now_ans.pop()
        return

47.全排列 II

遇到重复问题

从树的每一层解决源头性问题

使用used的表格

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        ans = []
        result = []
        use = [False for _ in nums]
        self.backtracking(nums, ans, result, use)
        return result

    def backtracking(self, nums, ans, result, use):
        if len(ans) == len(nums):
            result.append(ans[:])
            return 
        
        used = set()
        for i in range(len(nums)):
            if ((nums[i] in used) or use[i] ):
                continue
            used.add(nums[i])

            use[i] = True
            ans.append(nums[i])
            self.backtracking(nums, ans, result, use)
            use[i] = False
            ans.pop()

            

 


332.重新安排行程

依旧采用树的思想

想明白什么时候应该回溯

什么时候应该直接通过

可以考虑说返回bool类型进行判断

注意可以直接用sort进行排序

class Solution:
    def findItinerary(self, tickets: List[List[str]]) -> List[str]:
        tickets.sort()
        used = [True for _ in range(len(tickets))]
        result = ['JFK']

        self.backtracking(tickets, used, result, 'JFK',0)
        return result

    
    def backtracking(self, tickets, used, result, now, num):
        if(num == len(tickets)):
            return True

        for i in range(len(tickets)):
            temp = tickets[i]

            if used[i] and temp[0] == now:

                used[i] = False
                result.append(temp[1])
                if(self.backtracking(tickets, used, result, temp[1], num+1)):
                     return True
                result.pop()
                used[i] = True
        
        return False
        
                
                
from collections import defaultdict

class Solution:
    def findItinerary(self, tickets):
        targets = defaultdict(list)  # 创建默认字典,用于存储机场映射关系
        for ticket in tickets:
            targets[ticket[0]].append(ticket[1])  # 将机票输入到字典中
        
        for key in targets:
            targets[key].sort(reverse=True)  # 对到达机场列表进行字母逆序排序
        
        result = []
        self.backtracking("JFK", targets, result)  # 调用回溯函数开始搜索路径
        return result[::-1]  # 返回逆序的行程路径
    
    def backtracking(self, airport, targets, result):
        while targets[airport]:  # 当机场还有可到达的机场时
            next_airport = targets[airport].pop()  # 弹出下一个机场
            self.backtracking(next_airport, targets, result)  # 递归调用回溯函数进行深度优先搜索
        result.append(airport)  # 将当前机场添加到行程路径中

可以使用字典的倒叙进行解决这题, 并且可以使用pop和append进行机票是否使用的


51. N皇后

典中典的题目 哈哈哈

一次过

class Solution:
    # def checking(self, now, ans):
    #     n = len(ans)
    #     if n == 0:
    #         return True
    #     if now in ans:
    #         return False
    #     for i in range(n):
    #         if ans[i] == (now-n+i) or ans[i] == (now+n-i):
    #             return False
    #     return True

    # def backtracking(self, ans, used):
    #     #print(ans)
    #     if len(ans) == self.n:
    #         self.solution.append(ans)
    #         return 
    #     for i in range(self.n):
    #         if self.checking(i, ans):
    #             used[i] = True
    #             ans.append(i)
    #             self.backtracking(ans[:], used[:])
    #             used[i] = False
    #             ans.pop()
        
    # def solveNQueens(self, n: int) -> List[List[str]]:
    #     self.solution = []
    #     self.n = n
    #     used = [False for _ in range(n)]
    #     self.backtracking([], used)
    #     answer = []
    #     for i in self.solution:
    #         single_ans = []
    #         for j in i:
    #             line = "."*(j) + 'Q' + '.'*(n-j-1)
    #             single_ans.append(line)
    #         answer.append(single_ans)
    #     return answer

    def solveNQueens(self, n: int) -> List[List[str]]:
        result = []
        ans = []
        self.backtracking(result, ans, n)
        results = []
        for i in range(len(result)):
            temp = []
            for j in result[i]:
                s = "."*(j)+'Q'+'.'*(n-1-j)
                temp.append(s)
            results.append(temp[:])

        return results
    
    def backtracking(self, result, ans, n):
        if len(ans) == n:
            result.append(ans[:])
            return 
        for i in range(n):
            if self.checking(ans, i, n):
                ans.append(i)
                self.backtracking(result, ans, n)
                ans.pop()
        return 
    
    def checking(self, ans, k, n):
        if k in ans:
            return False
        l = len(ans)
        for i in range(l):
            left = k-i-1
            if left >= 0 and ans[l-i-1] == left:
                return False
            right = k+i+1
            if right < n and ans[l-i-1] == right:
                return False
        return True

 


37. 解数独

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        self.backtracking(0,0,board)
    
    def isValid(self, row, col, ch, board):
        row, col = int(row), int(col)
        for i in range(9):

            if board[i][col] == ch:
                return False

            if board[row][i] == ch:
                return False

            if board[3*(row//3)+i//3][3*(col//3)+i%3] == ch:
                return False

        return True

    def backtracking(self, row, col, board):
        if row == 9:
            return True

        if col == 9:
            return self.backtracking(row+1, 0, board)

        if board[row][col] == '.':

            for i in range(1, 10):
                if self.isValid(row, col, str(i), board):
                    board[row][col] = str(i)

                    if self.backtracking(row, col+1, board):
                        return True
                        
                    board[row][col] = '.'
            return False
        else:
            return self.backtracking(row, col+1, board)
                
    

 

 

 

 


总结

子集问题分析:一定要排序后去重

  • 时间复杂度:$O(n × 2^n)$,因为每一个元素的状态无外乎取与不取,所以时间复杂度为$O(2^n)$,构造每一组子集都需要填进数组,又有需要$O(n)$,最终时间复杂度:$O(n × 2^n)$。
  • 空间复杂度:$O(n)$,递归深度为n,所以系统栈所用空间为$O(n)$,每一层递归所用的空间都是常数级别,注意代码里的result和path都是全局变量,就算是放在参数里,传的也是引用,并不会新申请内存空间,最终空间复杂度为$O(n)$。

排列问题分析:

  • 时间复杂度:$O(n!)$,这个可以从排列的树形图中很明显发现,每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n * n-1 * n-2 * ..... 1 = n!。每个叶子节点都会有一个构造全排列填进数组的操作(对应的代码:result.push_back(path)),该操作的复杂度为$O(n)$。所以,最终时间复杂度为:n * n!,简化为$O(n!)$。
  • 空间复杂度:$O(n)$,和子集问题同理。

组合问题分析:

  • 时间复杂度:$O(n × 2^n)$,组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
  • 空间复杂度:$O(n)$,和子集问题同理。

 

 

 

 

 
 

 

posted on 2023-06-19 01:25  跪求个offer  阅读(50)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3