python 回溯法模板详解


Posted in Python onFebruary 26, 2020

什么是回溯法

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

无重复元素全排列问题

给定一个所有元素都不同的list,要求返回list元素的全排列。

设n = len(list),那么这个问题可以考虑为n叉树,对这个树进行dfs,这个问题里的回溯点就是深度(也就是templist的长度)为n时,回溯的条件就是当前元素已经出现在templist中了。

回溯法与递归:

回溯法是一种思想,递归是一种形式

class Solution(object):

  #rtlist用来存储所有的返回所有排列,templist用来生成每个排列
  def backtrack(self,rtlist,templist,nums):
    if(len(templist) == len(nums)):
      rtlist.append(templist[:])
    else:
      for i in nums:

        if(i in templist): #如果在当前排列中已经有i了,就continue,相当于分支限界,即不对当前节点子树搜寻了
          continue
        templist.append(i)
        self.backtrack(rtlist,templist,nums)
        templist.pop() #把结尾的元素用nums中的下一个值替换掉,遍历下一颗子树

  def permute(self,nums):
    rtlist = []
    templist = []
    self.backtrack(rtlist,templist,nums)
    return rtlist

nums=[1,2,3]时的树结构:

python 回溯法模板详解

关键的就是确定好分支限界以及回溯点。

这里面有一个问题就是每次递归时把新加入的元素从nums删除在递归可不可以,实际上这样的时间复杂度并不会减少太多,因为对list进行操作还需要一定的时间,而原解法中因为有分支限界所以时间复杂度也不会太差。

有重复元素全排列

这个问题和上面的区别主要在于分支限界的差别,不能在使用出现重复元素作为回溯条件了,否则所有的都不满足。

这里我们应该使用计数器记录nums中每个元素出现的次数,如果当前元素超过次数则返回,但是这里还有一个问题就是可能会出现同样的排列多次,这里的解决办法就是同一层不许出现重复元素,这里有两种解决办法,一种是直接传入distinct的数组,还有一种是使用一个集合记录当前层已使用的元素。

第一种方法:

from collections import Counter

class Solution(object):

  def backtrack(self, rtlist, tmplist, counter, nums, length):
    if len(tmplist) == length:#回溯点
      rtlist.append(tmplist[:])
    else:
      for i in nums:#横向遍历
        if counter[i] == 0:#分支限界
          continue

        counter[i] -= 1
        tmplist.append(i)
        self.backtrack(rtlist, tmplist, counter, nums, length)#纵向遍历
        counter[i] += 1
        tmplist.pop()

  def permuteUnique(self, nums):

    rtlist, tmplist, counter = [], [], Counter(nums)
    length = len(nums)
    self.backtrack(rtlist, tmplist, counter, list(set(nums)), length)

    return rtlist

第二种

from collections import Counter

class Solution(object):

  def backtrack(self, rtlist, tmplist, level, counter, nums):
    if len(tmplist) == len(nums):
      rtlist.append(tmplist[:])
    else:
      for i in nums:
        if i in level or counter[i] == 0:
          continue

        counter[i] -= 1

        tmplist.append(i)
        level.add(i)
        self.backtrack(rtlist, tmplist, set(), counter, nums)

        counter[i] += 1
        tmplist.pop()


  def permuteUnique(self, nums):
    if not nums:
      return []
    rtlist, tmplist, level, counter = [], [], set(), Counter(nums)
    self.backtrack(rtlist, tmplist, level, counter, nums)

    return rtlist

在递归时不能用“=”修改父函数的变量,因为“=”只能改变变量的指向,要修改父函数的变量要直接在内存中修改,例如放入容器中可以直接找到变量内存地址。通常使用container.method()。

例如在上面的程序中如果我们想要在回溯点把counter复原不能使用counter = Counter(nums),而是应该逐个修改counter[key]

总结

回溯法其实就是把原问题考虑成一棵树,我们遍历这棵树在不可能的地方返回,不在遍历这个节点的子树,在满足要求时返回。

所以在回溯法中,关键的就是找出合理的分支限界(重要),和返回条件。

更多请参考

多叉树的遍历方法:

def travel(root):
 遍历root
 for subtree_root in 当前层所有节点:

travel(subtree_root)

在for中对一层的所有节点都执行了travel,又因为对所有节点的所有子树都执行了travel,所以可以完成遍历。

以上这篇python 回溯法模板详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python实现的数据结构与算法之快速排序详解
Apr 22 Python
Python检测QQ在线状态的方法
May 09 Python
总结python爬虫抓站的实用技巧
Aug 09 Python
python多进程实现进程间通信实例
Nov 24 Python
Python语言描述KNN算法与Kd树
Dec 13 Python
基于Python socket的端口扫描程序实例代码
Feb 09 Python
django中模板的html自动转意方法
May 27 Python
Python 保存矩阵为Excel的实现方法
Jan 28 Python
django自定义非主键自增字段类型详解(auto increment field)
Mar 30 Python
使用jupyter notebook直接打开.md格式的文件
Apr 10 Python
python 使用递归的方式实现语义图片分割功能
Jul 16 Python
python利用opencv保存、播放视频
Nov 02 Python
python实现信号时域统计特征提取代码
Feb 26 #Python
Python 基于FIR实现Hilbert滤波器求信号包络详解
Feb 26 #Python
python实现逆滤波与维纳滤波示例
Feb 26 #Python
Python全面分析系统的时域特性和频率域特性
Feb 26 #Python
解决pycharm每次打开项目都需要配置解释器和安装库问题
Feb 26 #Python
Python中os模块功能与用法详解
Feb 26 #Python
Python中sys模块功能与用法实例详解
Feb 26 #Python
You might like
也谈php网站在线人数统计
2008/04/09 PHP
PHP 远程关机实现代码
2009/11/10 PHP
PHP 柱状图实现代码
2009/12/04 PHP
无法在发生错误时创建会话,请检查 PHP 或网站服务器日志,并正确配置 PHP 安装(win+linux)
2012/05/05 PHP
PHP 只允许指定IP访问(允许*号通配符过滤IP)
2014/07/08 PHP
php检测mysql表是否存在的方法小结
2017/07/20 PHP
php封装单文件上传到数据库(路径)
2017/10/15 PHP
PHP切割整数工具类似微信红包金额分配的思路详解
2019/09/18 PHP
Javascript中查找不以XX字符结尾的单词示例代码
2013/10/15 Javascript
JavaScript中的数学运算介绍
2014/12/29 Javascript
jQuery实现单击弹出Div层窗口效果(可关闭可拖动)
2015/09/19 Javascript
smartcrop.js智能图片裁剪库
2015/10/14 Javascript
简要了解jQuery移动web开发的响应式布局设计
2015/12/04 Javascript
JS异步加载的三种实现方式
2017/03/16 Javascript
es6在react中的应用代码解析
2017/11/08 Javascript
微信小程序wx.uploadfile 本地文件转base64的实现代码
2018/06/28 Javascript
在Angular中使用JWT认证方法示例
2018/09/10 Javascript
layui 图片上传+表单提交+ Spring MVC的实例
2019/09/21 Javascript
vue路由缓存的几种实现方式小结
2020/02/02 Javascript
在Echarts图中给坐标轴加一个标识线markLine
2020/07/20 Javascript
Js跳出两级循环方法代码实例
2020/09/22 Javascript
[01:14]2014DOTA2展望TI 剑指西雅图newbee战队专访
2014/06/30 DOTA
python33 urllib2使用方法细节讲解
2013/12/03 Python
python搭建服务器实现两个Android客户端间收发消息
2018/04/12 Python
python 除法保留两位小数点的方法
2018/07/16 Python
Python 私有化操作实例分析
2019/11/21 Python
Python实现常见的几种加密算法(MD5,SHA-1,HMAC,DES/AES,RSA和ECC)
2020/05/09 Python
python 解决mysql where in 对列表(list,,array)问题
2020/06/06 Python
用css3制作纸张效果(外翻卷角)
2013/02/01 HTML / CSS
Nike比利时官网:Nike.com (BE)
2019/02/07 全球购物
公务员总结性个人自我评价
2013/12/05 职场文书
领导的自我鉴定
2013/12/28 职场文书
英语简历自我评价
2014/01/26 职场文书
中国文明网向国旗敬礼寄语大全
2014/09/27 职场文书
导游词之安徽九华山
2019/09/18 职场文书
手把手教你导入Go语言第三方库
2021/08/04 Golang