《算法通关之路》-chapter15回溯法

《算法通关之路》学习笔记,记录一下自己的刷题过程,详细的内容请大家购买作者的书籍查阅。

全排列

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

class Solution:
    def permute(self, nums: list[int]) -> list[list[int]]:
        
        res = list()
        used = set()
        n = len(nums)

        def dfs(path: list[int]):
            
            if len(path) == n:
                res.append(path[:]) # 加入的是拷贝不是引用
                return
            for i in range(n):
                if i not in used:
                    used.add(i)
                    path.append(nums[i])
                    dfs(path)
                    path.pop()
                    used.remove(i)
            
        dfs([])
        return res

nums = [1,2,3]
solu = Solution()
solu.permute(nums)
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

全排列 II

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

class Solution:
    def permuteUnique(self, nums: list[int]) -> list[list[int]]:

        res = list()
        used = set()
        n = len(nums)
        nums.sort() # 提前排序

        def dfs(path: list[int]):
            
            if len(path) == n:
                res.append(path[:])
                return

            for i in range(n):
                # 同一个位置数字不能相同,i-1 not in used 表明这个位置i-1已经待过
                if i > 0 and nums[i] == nums[i-1] and i-1 not in used:
                    continue 
                if i not in used:
                    used.add(i)
                    path.append(nums[i])
                    dfs(path)
                    path.pop()
                    used.remove(i)
            
        dfs([])
        return res

nums = [1,1,2]
solu = Solution()
solu.permuteUnique(nums)
[[1, 1, 2], [1, 2, 1], [2, 1, 1]]

组合总和

力扣第39题
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/combination-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

# 无for循环写法,必须先排序
class Solution:
    def combinationSum(self, candidates: list[int], target: int) -> list[list[int]]:
        
        res = list()
        n = len(candidates)
        candidates.sort()

        def dfs(idx: int, cur: int, path: list[int]):

            if cur == 0: # 和为target
                res.append(path[:])
                return
            elif idx == n: # 遍历结束
                return
            
            if candidates[idx] <= cur:
                path.append(candidates[idx])
                dfs(idx, cur-candidates[idx], path)
                path.pop()
                dfs(idx+1, cur, path)
            else: # 没必要继续遍历
                return

        dfs(0, target, list())
        return res

candidates, target = [2,3,6,7], 7
solu = Solution()
solu.combinationSum(candidates, target)
[[2, 2, 3], [7]]
# for循环写法,是否排序都可
class Solution:
    def combinationSum(self, candidates: list[int], target: int) -> list[list[int]]:
        
        res = list()
        n = len(candidates)

        def dfs(idx: int, cur: int, path: list[int]):

            if cur == 0: # 和为target
                res.append(path[:])
                return
            
            for idx in range(idx, n):
        
                if candidates[idx] <= cur:
                    path.append(candidates[idx])
                    dfs(idx, cur-candidates[idx], path)
                    path.pop()

        dfs(0, target, [])
        return res


candidates, target = [2,3,6,7], 7
solu = Solution()
solu.combinationSum(candidates, target)
[[2, 2, 3], [7]]

组合总和 II

力扣第40题
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/combination-sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

# 不使用used的写法,依据i > idx来判断
class Solution:
    def combinationSum2(self, candidates: list[int], target: int) -> list[list[int]]:
     
        res = list()
        n = len(candidates)
        candidates.sort()
  

        def dfs(idx: int, cur: int, path: list[int]):

            if cur == 0: # 和为target
                res.append(path[:])
                return
            
            for i in range(idx, n):
                # for循环中去重,这里意味着舍弃了i-1所以不能再加入i
                if i > idx and candidates[i] == candidates[i-1]:
                    continue
                if candidates[i] <= cur:
                    path.append(candidates[i])
                    dfs(i+1, cur-candidates[i], path) # 每次进入下一层时不再考虑当前数字,避免多次使用
                    path.pop()
                else: 
                    break

        dfs(0, target, list())
        return res

candidates, target = [10,1,2,7,6,1,5], 8
solu = Solution()
solu.combinationSum2(candidates, target)
[[1, 1, 6], [1, 2, 5], [1, 7], [2, 6]]
# 使用used的写法
class Solution:
    def combinationSum2(self, candidates: list[int], target: int) -> list[list[int]]:
     
        res = list()
        n = len(candidates)
        candidates.sort()
        used = set()
  

        def dfs(idx: int, cur: int, path: list[int]):

            if cur == 0: # 和为target
                res.append(path[:])
                return
            
            for i in range(idx, n):
                # for循环中去重,这里意味着舍弃了i-1所以不能再加入i
                if i > 0 and candidates[i] == candidates[i-1] and i-1 not in used:
                    continue
                if candidates[i] <= cur:
                    path.append(candidates[i])
                    used.add(i)
                    dfs(i+1, cur-candidates[i], path)
                    path.pop()
                    used.remove(i)
                else: 
                    break

        dfs(0, target, list())
        return res

candidates, target = [10,1,2,7,6,1,5], 8
solu = Solution()
solu.combinationSum2(candidates, target)
[[1, 1, 6], [1, 2, 5], [1, 7], [2, 6]]

子集

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

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

# 不使用for循环的写法
class Solution:
    def subsets(self, nums: list[int]) -> list[list[int]]:
        res = []
        n = len(nums)

        def dfs(idx: int, path: list[int]):
            if idx == n:
                res.append(path[:])
                return
            path.append(nums[idx])
            dfs(idx+1, path)
            path.pop()
            dfs(idx+1, path)
        
        dfs(0, list())
        return res

nums = [1,2,3]
solu = Solution()
solu.subsets(nums)
[[1, 2, 3], [1, 2], [1, 3], [1], [2, 3], [2], [3], []]
# 使用for循环的写法
class Solution:
    def subsets(self, nums: list[int]) -> list[list[int]]:
        res = []
        n = len(nums)

        def dfs(idx: int, path: list[int]):

            res.append(path[:])
            for i in range(idx, n):
                path.append(nums[i])
                dfs(i+1, path)
                path.pop()
            
        dfs(0, list())
        return res

nums = [1,2,3]
solu = Solution()
solu.subsets(nums)
[[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]]

子集 II

力扣第90题
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/subsets-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

# 不使用used的写法,依据i > idx来判断
class Solution:
    def subsetsWithDup(self, nums: list[int]) -> list[list[int]]:
        res = []
        n = len(nums)
        nums.sort()

        def dfs(idx: int, path: list[int]):
            res.append(path[:])
            for i in range(idx, n):
                if i > idx and nums[i] == nums[i-1]:
                    continue
                path.append(nums[i])
                dfs(i+1, path)
                path.pop()
            
        dfs(0, list())
        return res

nums = [1,2,2]
solu = Solution()
solu.subsetsWithDup(nums)
[[], [1], [1, 2], [1, 2, 2], [2], [2, 2]]

笔记本-Github

猜你喜欢

转载自blog.csdn.net/cjw838982809/article/details/131924759
今日推荐