zoukankan      html  css  js  c++  java
  • 回溯

    参考:https://zhuanlan.zhihu.com/p/302415065

    关于回溯

    回溯是递归的“副产品”,并非高效算法,通过剪枝可以跳过非必要的搜索。

    回溯算法能解决如下问题:

    1. 组合问题:N个数里面按一定规则找出k个数的集合;
    2. 排列问题:N个数按一定规则全排列,有几种排列方式;
    3. 切割问题:一个字符串按一定规则有几种切割方式;
    4. 子集问题:一个N个数的集合里有多少符合条件的子集;
    5. 棋盘问题:N皇后,解数独等等。

    注:「组合是不强调元素顺序的,排列是强调元素顺序」,即组合无序,排列有序。

    回溯函数的伪代码模板

        def backtracking(self, 参数列表):
            # 定义递归出口
            if 终止条件:
                保存结果
                return
            for i in range(n):
                处理
                self.dfs(参数)
                处理回退
    

    以组合问题举例

    一句话总结:for循环横向遍历,递归纵向遍历,回溯不断调整结果集。

    例题:力扣77题:给定两个整数n和k,返回1...n中所有可能的k个数的组合。思路如下图:

    代码如下:

    class Solution(object):
        def __init__(self):
            self.res = []
    
        def combine(self, n, k):
            """
            :type n: int
            :type k: int
            :rtype: List[List[int]]
            """
            nums = [i for i in range(1, n+1)]
            sizeN = len(nums)
            self.dfs(nums, sizeN, k, 0, [])
            return self.res
    
        def dfs(self, nums, sizeN, k, begin, temp):
            # 定义递归出口:长度为k
            if len(temp) == k:
                self.res.append(temp)
            for i in range(begin, sizeN):
                if len(temp) > k:
                    break
                # 递归
                self.dfs(nums, sizeN, k, i+1, temp+[nums[i]])
    

    上述例题是在一个集合中求组合,需要用到“begin”这个变量来控制for循环的起始位置。

    那么对于组合问题,什么时候需要此变量呢?

    如果是一个集合来求组合,就需要begin,例如:力扣39、40、77等题;

    如果是多个集合取组合,各个集合之间相互不影响,那么就不需要begin,例如:力扣17题。

    做题中总结的一个模板

    class Solution():
        # 返回值,定义为全局变量
        def __init__(self):
            self.res = []
    
        def function(self, string):
            n = len(string)
            temp = []
            visit = [0 for _ in range(n)]
            self.dfs(string, temp, visit, n)
            return self.res
    
        def dfs(self, string, temp, visit, n):
            # 定义递归出口
            if len(temp) == len(string):
                self.res.append("".join(temp))
            else:
                # for循环横向遍历
                for i in range(n):
                    # 当且仅当当前字符未被访问
                    if not visit[i]:
                        # 用temp记录当前访问
                        temp.append(string[i])
                        visit[i] = 1
                        # 递归调用,纵向遍历
                        self.dfs(string, temp, visit, n)
                        # 回退
                        temp.pop(-1)
                        visit[i] = 0
    
  • 相关阅读:
    location.replace与location.href,location.reload的区别
    转载关于KeyPress和KeyDown事件的区别和联系
    Javascript中call的使用
    按值和按引用的比较
    理解cookie的path和domain属性
    HTML的快速写法:Emmet和Haml
    SVN标准命令
    linux常用命令
    Android4.0(Phone)拨号启动过程分析(一)
    Activity生命周期
  • 原文地址:https://www.cnblogs.com/panweiwei/p/14025143.html
Copyright © 2011-2022 走看看