【算法】回溯法刷题最全总结

刷题不总结复习,等于白刷。
最近越来越意识到这个道理,于是决定开始把刷过的容易忘记的题总结一下。

一开始看到一些资料中说回溯法是有框架可以遵循的,并不是十分的理解,后来看了几个题目以后,包括dfs的一些题,总算是有了一些体会。
总结一下的框架就是
def backtrace()
if 满足条件:
结果中加入track
for 选择 in 选择列表:
做选择
backtrace()进入下一级
撤销选择 重点

1、全排列问题

#1.1 全排列1
Leetcode46全排列
对于这个问题,也没什么好说的了,直接上代码吧!!!

def permuate(nums):
    def backtrace(nums,track,n):
        if len(track) == n:      #当track中达到n个,结束返回值到res中
            res.append(track.copy()) #这里注意要用track.copy()
            return
        for i,num in enumerate(nums):  #for 选择 in 选择列表:
            if num in track:          #这里属于附加条件,全排列问题要求不能重复
                continue
            track.append(num)        #做选择
            backtrace(nums,track,n)  #进入下一级
            track.pop()				 #撤销选择
    res=[]
    track=[]
    n=len(nums)
    backtrace(nums,track,n)
    return res
if __name__=='__main__':
    nums=[1,3,5]
    print(permuate(nums))

C++代码也是一样,也来一份吧。。。。

class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> track;
        backtrack(nums,track);
        return res;
    }

    void backtrack(vector<int> nums,vector<int> track){
        if(track.size()==nums.size()){
            res.push_back(track);
            return;
        }

        for(int i=0;i<nums.size();++i){
            if(find(track.begin(),track.end(),nums[i])!=track.end()) continue;
            track.push_back(nums[i]);
            backtrack(nums,track);
            track.pop_back();
        }
    }
};

你会了吗?

#1.2 全排列2
LeetCode47. 全排列 II
nums中有了重复的数字,这时候需要添加一个是否使用过的标志数组used,对nums做一个排列,然后在源代码的基础更改一下剪枝调剂即可,
[1,3,3],对于这样一个数组进行全排列,如果这个数字已经使用过(used[i]==1),那么重复,如果这个数字和前一个数字相同且前一位数字未使用过,也会重复,修改一下代码完成。

def permuate(nums):
    def backtrace(nums,track,used,n):
        if len(track) == n:
            res.append(track.copy())
            return
        for i,num in enumerate(nums):
            if used[i]:continue
            if i>0 and nums[i]==nums[i-1] and used[i-1]==0:
                continue
            used[i]=1
            track.append(num)
            backtrace(nums,track,used,n)
            used[i] = 0
            track.pop()
    res=[]
    track=[]
    nums.sort()
    n = len(nums)
    used=[0 for _ in range(n)]

    backtrace(nums,track,used,n)
    return res
if __name__=='__main__':
    nums=[1,3,3]
    print(permuate(nums))

在这里插入图片描述

2、组合问题

说完了全排列,再用公式来套一下组合问题吧。
#2.1LeetCode77组合
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。

def combine(n,k):
    def backtrace(st,n,track,k):
        if len(track) == k:
            res.append(track.copy())
            return
        for i in range(st,n+1):
            if i in track:
                continue
            track.append(i)
            backtrace(st+1,n,track,k)   #重点在这里,下一次回溯st+1
            track.pop()
    res=[]
    track=[]
    backtrace(1,n,track,k)
    return res
if __name__=='__main__':
    print(combine(4,2))

C++代码也是一样~~

class Solution {
public:
    vector<vector<int>> res;   
    vector<vector<int>> combine(int n, int k) {
        vector<int> track;
        backtrack(1,n,k,track);
        return res;
    }

    void backtrack(int first,int ed,int k,vector<int> &track ){
        if(track.size()==k)res.push_back(track);
        for(int i=first;i<=ed;++i){
            track.push_back(i);
            backtrack(i+1,ed,k,track);
            track.pop_back();
        }
    }
};

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

candidates 中的数字可以无限制重复被选取。
一样的套路,直接上代码吧。。。。

class Solution:
    def combinationSum(self, nums: List[int], target: int) -> List[List[int]]:
        def backtrace(nums,track,target):
            if sum(track) > target:
                return
            tmp=track.copy()
            tmp.sort()
            if sum(track) == target and tmp not in res:
                res.append(tmp)
                return
            for i, num in enumerate(nums):
                
                track.append(num)
                backtrace(nums,track,target)
                track.pop()
        res=[]
        track=[]
        backtrace(nums,track,target)
        return res

##2.2 组合总和2
稍微加一点改动,加一个used数组记录该数字是否被使用过,即可。

class Solution:
    def combinationSum2(self, nums: List[int], target: int) -> List[List[int]]:
        def backtrace(nums,track,target):
            if sum(track) > target:
                return
            tmp=track.copy()
            tmp.sort()
            if sum(track) == target and tmp not in res:
                res.append(tmp)
                return
            for i, num in enumerate(nums):
                if used[i]:   #如果数字被用过,跳过
                    continue
                track.append(num)
                used[i]=1
                backtrace(nums,track,target)   
                track.pop()
                used[i]=0
        res=[]
        track=[]
        used=[0 for _ in range(len(nums))]
        backtrace(nums,track,target)
        return res

##2.3电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
有了上面的两题做铺垫,这个题目应该是小case了吧,让我来试试。。。

def letterCombinations(digits):
    phone = {'2': ['a', 'b', 'c'],
             '3': ['d', 'e', 'f'],
             '4': ['g', 'h', 'i'],
             '5': ['j', 'k', 'l'],
             '6': ['m', 'n', 'o'],
             '7': ['p', 'q', 'r', 's'],
             '8': ['t', 'u', 'v'],
             '9': ['w', 'x', 'y', 'z']}

    def backtrace(digits,track,st,n):
        if len(track) == n:
            res.append(track)
            return
        digit=phone[digits[st]]
        for letter in digit:
            track += letter
            backtrace(digits, track, st+1, n)
            track = track[0:-1]
    res = []
    n = len(digits)
    if digits:
        backtrace(digits, "", 0, n)
    return res

C++代码也一样啊

class Solution {
public:
	vector<string> res;
	unordered_map<char, string> phone{
            {'0', " "}, {'1',"*"}, {'2', "abc"},
            {'3',"def"}, {'4',"ghi"}, {'5',"jkl"},
            {'6',"mno"}, {'7',"pqrs"},{'8',"tuv"},
            {'9',"wxyz"}};  
    vector<string> letterCombinations(string digits) {
        int n=digits.size();
        
        if(digits == "") return res;
        backtrace(digits, "", n,0);
        return res;
    }
    
    void backtrace( string &digits, string track, int n,int k){
        if(track.size() == n){
            res.push_back(track);
            return;
        }
        string tmp = phone[digits[k]];
        for(char w : tmp){
            track += w;
            backtrace(digits,track,n, k+1);
            track.pop_back();
        }
        return ;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值