【回溯法】python 实现 全排列,子集,组合问题、分割回文串

leetcode39、40、47、 78、 77、131

目录

47:全排列

78:子集

39组合总和

40 组合总和

77组合

131 分割回文串


47:全排列

 全排列问题:
 使用回溯法,需要注意的问题:
 不能重复出现,要去重,可以先将数字排序,这样重复的元素肯定是相邻的, 那么我们判断前一个元素和当前元素相同,就conutinue,还有就是用过一个元素 就对这个元素做一个标记,这样看到上一个元素的标记,就不会重复使用了

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


def permuteUnique(nums:list) ->list:
    res=[]
    nums.sort()
    used=[0 for i in range(len(nums))]
    def backtrack(nums,used,path):
        for i in range(len(nums)):
            if len(path)==len(nums):
                res.append(path[:])
                return
            if used[i]==1:  #用过
                continue
            if i>0 and nums[i]==nums[i-1] and used[i-1]==0: #重复出现的数字,且没有被用过
                continue
            used[i]=1
            # path.append(nums[i])
            backtrack(nums,used,path+[nums[i]])   #path:list nums[i]:int
            used[i]=0
    backtrack(nums,used,[])
    return res
print(permuteUnique([1,1,3]))

78:子集
 

给你一个整数数组nums ,数组中的元素互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

def subset(nums:list) -> list:
    res=[]
    def backtrack(path,start):
        res.append(path[:])
        for i in range(start,len(nums)):
            backtrack(path+[nums[i]],i+1)
            # return res
    backtrack([],0)
    return res
print(subset([1,2,3]))

39组合总和

---->回溯算法
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
注意:!!集合元素可重复选取,子集,candicate内元素无序,无重复


result=list()
path=list()
def MakeSum(target:int,candidates:list) ->list:
    def backtrack(target,candidates,sum,start):
        if sum>target:
            return
        if sum==target:
            result.append(path[:])
            return
        for i in range(start,len(candidates)):
            path.append(candidates[i])
            sum+=candidates[i]
            backtrack(target,candidates,sum,i)
            sum-=candidates[i]
            path.remove(candidates[i])
    backtrack(target,candidates,0,0)  #sum,start=0,0
    return result
print(MakeSum(7,[2,3,6,7])

40 组合总和

回溯
输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]
即集合内元素不能被重复,比如这里有一个1,那么一个列表内只能有1个1,1不可以重复使用


class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        result=[]
        path=[]
        candidates.sort()
        def backtrack(target,candidates,sum,start):
            # if sum>target:
            #     return
            if sum==target:
                result.append(path[:])
                return
            for i in range(start,len(candidates)):
                if sum+candidates[i]>target:
                    break
                # if sum + candidates[i] <= target:
                if i > start and (candidates[i] == candidates[i - 1]):
                    continue
                if (sum + candidates[i] <= target):
                    sum+=candidates[i]
                    path.append(candidates[i])
                    backtrack(target,candidates,sum,i+1)
                    sum-=candidates[i]
                    path.pop()  #sum,start=0,0

        backtrack(target,candidates,0,0)
        return result

77组合


给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]


from typing import List
def combine(n: int, k: int) -> List[List[int]]:
    res=[]
    nums=[i for i in range(1,n+1)]
    def backtrack(nums,path,start):
        if len(path)==k:
            # res.append(path) 加入的是一个空列表
            res.append(path[:])
            return
        for j in range(start,n+1-(k-len(path))):  #加入了剪枝操作
         # for j in range(start,len(nums)):  #未加入剪枝操作
            path.append(nums[j])
            backtrack(nums,path,j+1)
            # path.remove(nums[j]) remove(value) 会从列表第一个元素开始,直到value,把value移除,整个列表的移动,因此效率没pop高
            path.pop()   #回溯   pop(index) 默认pop的是最后一个元素,有返回值返回值是pop出的元素值
    backtrack(nums,[],0)
    return res
print(combine(n=4,k=2)

131 分割回文串

分割回文串 力扣131
示例 1:
输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]
回溯法

# print([1]+[2])
def partition(s:str) ->list:
    res=[]
    def ispalindrome(x):
        if x[::-1]==x:
            return True
    def backtrack(start,path):
        if start==len(s):  #叶子节点收集path
            res.append(path[:])
            return
        for i in range(start,len(s)):
            if ispalindrome(s[start:i+1]):
                path.append(s[start:i+1])
            else:
                continue
            backtrack(i+1,path)
            path.pop()
    backtrack(0,[])
    return res
print(partition('aab'))

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
旅行商问题一个经典的组合优化问题,描述为一个推销员需要从一个城市出发,经过所有城市后回到出发地,如何选择行进路线使得总行程最短。引用回溯算法是一种类似于枚举的搜索尝试过程,它通过不断搜索和回溯来寻找问题的解。在解决旅行商问题时,可以使用回溯算法来穷举所有可能的路径并找到最优解。引用 关于使用Python中的回溯法解决旅行商问题,可以参考引用中的模板和实现步骤。这个模板使用了子集树来表示可能的路径,并通过回溯的方式逐步构建路径并计算总行程,最终找到最优解。在实现过程中,需要注意剪枝策略来减少搜索空间,以提高算法效率。具体的操作技巧和实现步骤可以参考引用中的详细说明。 总之,使用Python中的回溯法可以解决旅行商问题,通过穷举所有可能的路径,并根据特定的条件选择最优解。可以参考引用中提供的模板和实现步骤,根据具体的问题进行调整和优化。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Python基于回溯法子集树模板解决旅行商问题(TSP)实例](https://download.csdn.net/download/weixin_38606294/12872315)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [算法课堂实验报告(五)——python回溯法与分支限界法(旅行商TSP问题)](https://blog.csdn.net/Campsisgrandiflora/article/details/82114198)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值