【Leetcode】回溯-递归系列

【Leetcode-17】

一、题目:电话号码的字母组合

  给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

  给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

二、代码:

def letterCombinations(self, digits: str) -> List[str]:
        res_all = []
        if len(digits) == 0:
            return res_all
        def back_up(pre, arr):
            if len(arr) == 0:
                res_all.append(pre)
                return 
            # 不加入
            # back_up(pre, arr[1:])
            # 加入
            letters = list(look_up[arr[0]])
            for i in range(len(letters)):
                letters[0], letters[i] = letters[i], letters[0]
                back_up(pre+letters[0], arr[1:])
                letters[i], letters[0] = letters[0], letters[i]

        
        look_up = {
            "2": "abc", "3": "def", "4": "ghi", "5": "jkl", "6": "mno", "7": "pqrs",
            "8": "tuv", "9": "wxyz"
        }
        back_up('', digits)
        return res_all

 

【Leetcode-22】

一、题目:括号生成

  数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

二、代码

def generateParenthesis(self, n: int) -> List[str]:
        res = []
        def back_up(pre, left, right):
            if left == 0 and right == 0:
                res.append(pre)

            if left > 0:
                back_up(pre+'(', left-1, right)

            if left < right:  # 左放的多,可放右
                back_up(pre+')', left, right-1)

        back_up('', n, n)
        return res

 

【Leetcode-46】

一、题目:全排列

  给定一个 没有重复 数字的序列,返回其所有可能的全排列。

二、代码:

def permute(self, nums: List[int]) -> List[List[int]]:
        res = []
        if len(nums) == 0:
            return res
        
        def back(pre, arr):
            if len(arr) == 0:
                res.append(pre[:]) 
            for i in range(len(arr)):
                arr[i], arr[0] = arr[0], arr[i]
                pre.append(arr[0])
                back(pre, arr[1:])
                pre.pop()
                arr[0], arr[i] = arr[i], arr[0]
            
        back([], nums)
        return res

 

【Leetcode-39】

一、题目:组合总数

  给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

  candidates 中的数字可以无限制重复被选取。

二、代码:

def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        n = len(candidates)
        res = []
        if n == 0:
            return res

        def back(pre, arr, t):
            if len(arr) == 0 or t < 0:  # 放完了
                if len(arr) == 0 and t == 0:
                    res.append(pre[:])
                return
            # 不放
            back(pre, arr[1:], t)
            #
            if t-arr[0] >= 0:
                pre.append(arr[0])
                back(pre, arr, t-arr[0])
                pre.pop()


        back([], candidates, target)
        return res

 

【Leetcode-51】

一、题目:N皇后

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

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

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

  要求皇后不能在同一行、同一列、对角。

二、代码:

def solveNQueens(self, n: int) -> List[List[str]]:
        """
        每一行往下放,放的时候检查冲突,如果冲突则不放,冲突记录为已经放的列,已经放的对角线(i-j和i+j分别表示两条对角线)
        """
        res = []
        def back_up(i, pre_res, pre_cols, pre_diags1, pre_diags2):
            # 输入为当前要放的行,前面行放完后的结果,前面行已经放的列,前面行已经放的对角线(行列差值表示对角线)
            if i == n: # 已经放完了
                res.append(pre_res[:])
                return
            # 往i行放,若已经冲突则直接返回
            for j in range(n):
                if j in pre_cols or i - j in pre_diags1 or i+j in pre_diags2:  # 该列/对角已经放过
                    continue
                # 该行放在第j列,其他列为'.'
                res_item = ['.'] * n
                res_item[j] = 'Q'
                res_item = "".join(res_item)
                pre_res.append(res_item)
                pre_cols.add(j)
                pre_diags1.add(i-j)
                pre_diags2.add(i+j)
                back_up(i+1, pre_res, pre_cols, pre_diags1, pre_diags2)
                # 尝试放其他列
                pre_res.pop()
                pre_cols.remove(j)
                pre_diags1.remove(i-j)
                pre_diags2.remove(i+j)
        back_up(0, [], set(), set(), set())
        return res

 

【Leetcode-78】

一、题目:子集

  给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

  解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

二、代码:

def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        n = len(nums)
        if n == 0:
            return [[]]
        def back(pre, arr):
            if len(arr) == 0:
                res.append(pre[:])
                return
            #
            pre.append(arr[0])
            back(pre, arr[1:])
            pre.pop()

            # 不放
            back(pre, arr[1:])
        
        back([], nums)
        return res
        

 

【Leetcode-77】

一、题目:组合

  给定两个整数 n 和 k,返回 1 ... 中所有可能的 k 个数的组合。

二、代码:

def combine(self, n: int, k: int) -> List[List[int]]:
        res = []
        '''
        # 下面是求排列组合,考虑了顺序,思路是每个坑位必须放元素,每次尝试放不同元素,直到填满坑位
        def back_up(pre, arr, left):
            # 已经放的,剩下的元素, 剩下几个坑位
            if left == 0:
                res.append(pre[:])
                return
            # 放元素
            for i in range(len(arr)):
                arr[0], arr[i] = arr[i], arr[0]
                pre.append(arr[0])
                back_up(pre, arr[1:], left-1)
                pre.pop(-1)
                arr[i], arr[0] = arr[0], arr[i]
        '''
        # 下面是求组合,不考虑顺序,思路是对元素1-n挨个放,每个元素都有放和不放两种选择,直到坑位满或者元素不够
        def back_up(pre, arr, left):
            # 已经放的,剩下的元素, 剩下几个坑位
            if left == 0:
                res.append(pre[:])
                return
            if len(arr) < left:  # 剩下的元素太少,坑位太多哦
                return
            # 放当前元素,每个元素有放和不放两种
            #
            pre.append(arr[0])
            back_up(pre, arr[1:], left-1)
            pre.pop(-1)
            # 不放
            back_up(pre, arr[1:], left)
        if k > n:
            return res
        arr = [t+1 for t in range(n)]
        back_up([], arr, k)
        return res

 

【Leetcode-79】

一、题目:单词搜索

  给定一个二维网格和一个单词,找出该单词是否存在于网格中。

  单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

二、代码:

def exist(self, board: List[List[str]], word: str) -> bool:
        neibors = [[-1, 0], [1, 0], [0, -1], [0, 1]]
        m, n = len(board), len(board[0])
        flag = [[True]*n for _ in range(m)]
        def dfs(i, j, k):
            if board[i][j] != word[k]:
                return False
            if k == len(word) - 1:
                return True
            flag[i][j] = False
            matched = False
            for pos in neibors:
                x, y = pos
                new_i, new_j = x + i, j + y
                if 0 <= new_i < m and 0 <= new_j < n and flag[new_i][new_j]:
                    if dfs(new_i, new_j, k+1):
                        matched = True
                        break
            flag[i][j] = True
            return matched
            

        for i in range(m):
            for j in range(n):
                if dfs(i, j, 0):
                    return True
        return False

 

【Leetcode-93】

一、题目:复原IP地址

  给定一个只包含数字的字符串,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 IP 地址 。你可以按任何顺序返回答案。

  有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

  例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。

二、代码:

def restoreIpAddresses(self, s: str) -> List[str]:
        """
        回溯
        """
        total_cnt = 4
        max_len = 3  # 某一段最长为3
        res = []
        def back(pre, cnt, s):
            # 前面已经放好的结果,本次放第几段,剩下元素
            if cnt > total_cnt:  # 段数够了
                if len(s) == 0:  # 元素也放完了
                    res.append('.'.join(pre))
                return
            # 剪枝,剩下的元素太多或太少
            if len(s) < total_cnt-cnt+1 or len(s) / max_len > total_cnt-cnt+1:
                return
            # 放该段元素,若前缀为0则只能放0
            if s[0] == '0':
                pre.append('0')
                back(pre, cnt+1, s[1:])
                pre.pop(-1)
            else:
                # 逐渐截出来尝试放
                for this_len in range(1, max_len+1):
                    if len(s) < this_len:  # 剩下的都没这么长
                        continue
                    item = s[:this_len]
                    if int(item) <= 255:
                        pre.append(item)
                        back(pre, cnt+1, s[this_len:])
                        pre.pop(-1)
        back([], 1, s)
        return res

 

posted @ 2021-04-01 21:53  我若成风者  阅读(161)  评论(0)    收藏  举报