当前位置: 首页 > news >正文

代码随想录算法训练营第三十天|491. 非递减子序列,46. 全排列,47. 全排列 II,332. 重新安排行程,51. N 皇后,37. 解数独

491. 非递减子序列,46. 全排列,47. 全排列 II,332. 重新安排行程,51. N 皇后,37. 解数独

    • 491. 非递减子序列
    • 46. 全排列
    • 47. 全排列 II
    • 332. 重新安排行程
    • 51. N 皇后
    • 37. 解数独

491. 非递减子序列

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

示例 1
输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

示例 2
输入:nums = [4,4,3,2,1]
输出:[[4,4]]

注意这道题数组不能排序,会破坏数组结构。有重复元素在同一图层的情况,则同一父节点下的同层上使用过的元素就不能再使用了,参考代码使用数组来进行去重操,记录本层元素是否重复使用,新的一层uset都会重新定义(清空),所以要知道uset只负责本层

tips:是否增加return是判断是否输出到叶节点。回溯题目在做时候,先画树便于理解,注意同一图层和同一分支元素是哪里生效的,下面一题增加了同一分支的判断。

class Solution:
    def findSubsequences(self, nums: List[int]) -> List[List[int]]:
        result = []
        self.backtracking(nums,0,[],result)
        return result    
    
    def backtracking(self,s,index,path,result):
        if  len(path)>1:
            result.append(path[:])
        used = [0] * 201  # 使用数组来进行去重操作,题目说数值范围[-100, 100]
        for i in range(index,len(s)):           
            if (path and s[i] < path[-1]) or used[s[i] + 100] == 1:
                continue  # 如果当前元素小于上一个元素,或者已经使用过当前元素,则跳过当前元素
            
            used[s[i] + 100] = 1  # 标记当前元素已经使用过
            path.append(s[i])  # 将当前元素加入当前递增子序列
            self.backtracking(s, i + 1, path, result)
            path.pop()

46. 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2
输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3
输入:nums = [1]
输出:[[1]]
全排列,每次回溯从0开始,增加一个判断当前节点是否遍历过的判断,可使用字典或索引列表,下面是使用字典的例子,当前遍历值在输出数字 p a t h path path中则跳过。最终输出结果是 p a t h path path数组长度等于原数组长度。

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        result = []
        self.backtracking(nums,0,[],result)
        return result    
    
    def backtracking(self,s,index,path,result):
        if  len(path)==len(s):
            result.append(path[:])  
        for i in range(index,len(s)):  
            if s[i] in path:
                continue
            path.append(s[i])  
            self.backtracking(s, 0, path, result)
            path.pop()

参考代码用use数组记录是否使用。

class Solution:
    def permute(self, nums):
        result = []
        self.backtracking(nums, [], [False] * len(nums), result)
        return result

    def backtracking(self, nums, path, used, result):
        if len(path) == len(nums):
            result.append(path[:])
            return
        for i in range(len(nums)):
            if used[i]:
                continue
            used[i] = True
            path.append(nums[i])
            self.backtracking(nums, path, used, result)
            path.pop()
            used[i] = False

47. 全排列 II

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1
输入:nums = [1,1,2]
输出
[[1,1,2],
[1,2,1],
[2,1,1]]

示例 2
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

增加一个数组记录索引,同一图层重复元素判断用判断回溯重复数字的used数组。

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        result = []
        nums.sort()
        self.backtracking(nums,[],[],result)
        return result    
    
    def backtracking(self,s,path,root,result):
        if  len(path)==len(s):
            result.append(path[:]) 
            return result
        used = [0]* 20
        for i in range(len(s)):  
            if i in root or used[s[i]+10]==1:
                continue
            path.append(s[i])  
            root.append(i)
            used[s[i]+10]=1
            self.backtracking(s, path, root,result)           
            path.pop()
            root.pop()

参考代码,只用一个数组进行回溯,注意跳过循环的情况。

class Solution:
    def permuteUnique(self, nums):
        nums.sort()  # 排序
        result = []
        self.backtracking(nums, [], [False] * len(nums), result)
        return result

    def backtracking(self, nums, path, used, result):
        if len(path) == len(nums):
            result.append(path[:])
            return
        for i in range(len(nums)):
            if (i > 0 and nums[i] == nums[i - 1] and not used[i - 1]) or used[i]:
                continue
            used[i] = True
            path.append(nums[i])
            self.backtracking(nums, path, used, result)
            path.pop()

接下来是回溯题目几道比较难的应用,先学习一下思路。

332. 重新安排行程

给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。
所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

  • 例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前。

假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

示例 1
输入:tickets = [[“MUC”,“LHR”],[“JFK”,“MUC”],[“SFO”,“SJC”],[“LHR”,“SFO”]]
输出:[“JFK”,“MUC”,“LHR”,“SFO”,“SJC”]

示例 2
输入:tickets = [[“JFK”,“SFO”],[“JFK”,“ATL”],[“SFO”,“ATL”],[“ATL”,“JFK”],[“ATL”,“SFO”]]
输出:[“JFK”,“ATL”,“JFK”,“SFO”,“ATL”,“SFO”]
解释:另一种有效的行程是 [“JFK”,“SFO”,“ATL”,“JFK”,“ATL”,“SFO”] ,但是它字典排序更大更靠后。

class Solution:
    def findItinerary(self, tickets: List[List[str]]) -> List[str]:
        self.adj = {}

        # sort by the destination alphabetically
        # 根据航班每一站的重点字母顺序排序
        tickets.sort(key=lambda x:x[1])

        # get all possible connection for each destination
        # 罗列每一站的下一个可选项
        for u,v in tickets:
            if u in self.adj: self.adj[u].append(v)
            else: self.adj[u] = [v]

        # 从JFK出发
        self.result = []
        self.dfs("JFK")  # start with JFK

        return self.result[::-1]  # reverse to get the result

    def dfs(self, s):
        # if depart city has flight and the flight can go to another city
        while s in self.adj and len(self.adj[s]) > 0:
            # 找到s能到哪里,选能到的第一个机场
            v = self.adj[s][0]  # we go to the 1 choice of the city
            # 在之后的可选项机场中去掉这个机场
            self.adj[s].pop(0)  # get rid of this choice since we used it
            # 从当前的新出发点开始
            self.dfs(v)  # we start from the new airport

        self.result.append(s)  # after append, it will back track to last node, thus the result list is in reversed order

51. N 皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

示例 1
输入:n = 4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2
输入:n = 1
输出:[[“Q”]]

回溯经典应用N皇后问题,主要是isValid函数。判断当前位置插入是否有效,终止条件是遍历到最后一行或一列,此时判断已经插入的棋子数量,决定输出。

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        chessboard = ['.' * n for _ in range(n)]  # 初始化棋盘
        result = []
        path = []
        self.dfs(n, result, path, 0, 0, chessboard)
        return result

    def dfs(self, n, result, path, istart, j, chessboard):
        if istart == n or j == n:
            if len(path) == n:
                result.append(chessboard[:])
        for i in range(istart, n):
            for j in range(n):
                if self.isValid(n, i, j, path):
                    chessboard[i] = chessboard[i][:j] + 'Q' + chessboard[i][j + 1:]
                    path.append([i, j])
                    self.dfs(n, result, path, i + 1, j, chessboard)
                    chessboard[i] = chessboard[i][:j] + '.' + chessboard[i][j + 1:]
                    path.pop()

    def isValid(self, n, i, j, path):
        for t in path:
            if t[0] == i or t[1] == j:
                return False
        ii, jj = i - 1, j - 1
        # 检查 45 度角是否有皇后
        while ii >= 0 and jj >= 0:
            if [ii, jj] in path:
                return False  # 左上方向已经存在皇后,不合法
            ii -= 1
            jj -= 1
        # 检查 135 度角是否有皇后
        ii, jj = i - 1, j + 1
        while ii >= 0 and jj < n:
            if [ii, jj] in path:
                return False  # 右上方向已经存在皇后,不合法
            ii -= 1
            jj += 1
        return True

附参考代码

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        result = []  # 存储最终结果的二维字符串数组

        chessboard = ['.' * n for _ in range(n)]  # 初始化棋盘
        self.backtracking(n, 0, chessboard, result)  # 回溯求解
        return [[''.join(row) for row in solution] for solution in result]  # 返回结果集

    def backtracking(self, n: int, row: int, chessboard: List[str], result: List[List[str]]) -> None:
        if row == n:
            result.append(chessboard[:])  # 棋盘填满,将当前解加入结果集
            return

        for col in range(n):
            if self.isValid(row, col, chessboard):
                chessboard[row] = chessboard[row][:col] + 'Q' + chessboard[row][col+1:]  # 放置皇后
                self.backtracking(n, row + 1, chessboard, result)  # 递归到下一行
                chessboard[row] = chessboard[row][:col] + '.' + chessboard[row][col+1:]  # 回溯,撤销当前位置的皇后

    def isValid(self, row: int, col: int, chessboard: List[str]) -> bool:
        # 检查列
        for i in range(row):
            if chessboard[i][col] == 'Q':
                return False  # 当前列已经存在皇后,不合法

        # 检查 45 度角是否有皇后
        i, j = row - 1, col - 1
        while i >= 0 and j >= 0:
            if chessboard[i][j] == 'Q':
                return False  # 左上方向已经存在皇后,不合法
            i -= 1
            j -= 1

        # 检查 135 度角是否有皇后
        i, j = row - 1, col + 1
        while i >= 0 and j < len(chessboard):
            if chessboard[i][j] == 'Q':
                return False  # 右上方向已经存在皇后,不合法
            i -= 1
            j += 1

        return True  # 当前位置合法

37. 解数独

编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:

  • 数字 1-9 在每一行只能出现一次。
  • 数字 1-9 在每一列只能出现一次。
  • 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例 1
在这里插入图片描述
输入:board = [[“5”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”],
[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”],
[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”],
[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”],
[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”],
[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”],
[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”],
[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”],
[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]
输出:[[“5”,“3”,“4”,“6”,“7”,“8”,“9”,“1”,“2”],
[“6”,“7”,“2”,“1”,“9”,“5”,“3”,“4”,“8”],
[“1”,“9”,“8”,“3”,“4”,“2”,“5”,“6”,“7”],
[“8”,“5”,“9”,“7”,“6”,“1”,“4”,“2”,“3”],
[“4”,“2”,“6”,“8”,“5”,“3”,“7”,“9”,“1”],
[“7”,“1”,“3”,“9”,“2”,“4”,“8”,“5”,“6”],
[“9”,“6”,“1”,“5”,“3”,“7”,“2”,“8”,“4”],
[“2”,“8”,“7”,“4”,“1”,“9”,“6”,“3”,“5”],[“3”,“4”,“5”,“2”,“8”,“6”,“1”,“7”,“9”]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:

在这里插入图片描述

相关文章:

  • Robust多模态模型的开发
  • JS设计模式之职责链模式:优雅地处理请求流程
  • 【大数据】Hive快速入门
  • 【工具】前端js数字金额转中文大写金额
  • git的提取和拉取有啥区别
  • LeetCode整数转罗马数字
  • 使用NumPy进行线性代数的快速指南
  • 内网Debian\Ubuntu服务器安装dep包,基于apt-rdepends下载相关依赖
  • Reverse Tabnabbing漏洞的理解和利用
  • SQLite数据库
  • 前端 | Uncaught (in promise) undefined
  • 通过Express + Vue3从零构建一个用户认证与授权系统(一)项目结构设计
  • 241007深度学习之LeNet
  • C++入门基础知识107—【关于C++continue 语句】
  • Pikachu-Sql Inject-数字型注入(GET)
  • 小猿口算脚本
  • 2024面试自动化测试面试题【含答案】
  • Linux安装conda
  • 我服了,上班在 Excel 里都能摸鱼。。。
  • 爬虫prc技术----小红书爬取解决xs