“递归只应天上有,迭代还须在人间”,从这句话我们可以看出递归的精妙,确实厉害,递归是将问题规模逐渐减小,

然后再反推回去,但本质上是从最小的规模开始,直到目标值,思想就是数学归纳法,举个例子,求阶乘 N!=(N-1)!*N ,

而迭代是数学中的极限思想,利用前次的结果,逐渐靠近目标值,迭代的过程中规模不变,举例如For循环,直到终止条件。

递归的思想不复杂,但代码理解就麻烦了,要理解一个斐波那契数组递归也不难,比如下面的回溯算法递归,for 循环里面

带递归,看代码是不是晕了?好,下面我们专门来聊聊这个框架!

作者原创文章,谢绝一切形式转载,违者必究!

准备:

Idea2019.03/JDK11.0.4

难度: 新手--战士--老兵--大师

目标:

回溯算法分析与应用

1 回溯算法

先给出个回溯算法框架:

backtrack(路径,选择列表){

//结束条件

将中间结果加入结果集

for 选择 in 选择列表:

//做选择,并将该选择从选择列表中移除

路径.add(选择)

backtrack(路径,选择列表)

//撤销选择

路径.remove(选择)

}

为了理解上述算法,回想一下,我前篇文章中有说到,多路树的遍历算法框架:

private static class Node {

public int value;

public Node[] children;

}

public static void dfs(Node root){

if (root == null){

return;

}

// 前序遍历位置,对node做点事情

for (Node child:children

) {

dfs(child);

}

// 后序遍历位置,对node做点事情

}

如果去掉路径增加/撤销的逻辑,是不是和多路树的遍历算法框架一样了呢?其实就是一个多路树DFS的变种算法!

另外,虽然递归代码的理解难度大,运行时是栈实现,但看官不要掉进了递归栈,否则就出不来了,如果试着用打断

点逐行跟进的办法非要死磕,那对不起,估计三顿饭功夫也可能出不来,甚至我怀疑起自己的智商来,所以,理解递归,

核心就是抓住函数体来看,抽象的理解,只看懂 N 和 N-1 的转移逻辑即可!不懂的先套用再说,也不定哪天就灵感来了,

一下顿悟!

那就先上菜了!先是经典回溯算法,代号A,我们要做个数组全排列,我看别人说回溯算法也都是拿这个例子说事,

我就落个俗套:

class Permutation {

// 排列组合算法

private static List> output = new LinkedList();

static List> permute( List nums, // 待排列数组

int start //起始位置

){

if (start == nums.size()){

output.add(new ArrayList<>(nums));

}

for (int i = start; i < nums.size(); i++) {

// 做选择,交换元素位置

Collections.swap(nums, start, i);

// 递归,缩小规模

permute( nums,start +1);

// 撤销选择,回溯,即恢复到原状态,

Collections.swap(nums, start, i);

}

return output;

}

// 测试

public static void main(String[] args) {

List nums = Arrays.asList(1,2,3,4);

List> lists = permute(nums,0);

lists.forEach(System.out::println);

}

}

代码理解:数组 {1,2,3} 的全排列,我们马上知道有{1,2,3},{1,3,2},{2,1,3},{2,3,1},{3,1,2},{3,2,1}排列,具体过程就是通过递归缩小规模,

做 {1,2,3} 排列,先做 {2,3} 排列,前面在加上 1 即可,继续缩小,就是做 {3} 的排列。排列就是同一个位置把所有不同的数都放一次,

那么代码实现上可使用交换元素法,比如首个位置和所有元素都交换一遍,不就是全部可能了吗。这样,首个位置所有可能就遍历了

一遍,然后在递归完后,恢复(回溯)一下,就是说每次交换都是某一个下标位置,去交换其他所有元素。

再来个全排列的算法实现,代号B,也是使用回溯的思想:

public class Backtrack {

public static void main(String[] args) {

int[] nums = {1,2,3,4};

List track = new LinkedList<>();

List> res = backtrack(nums,track);

System.out.println(res);

}

// 存储最终结果

private static List> result = new LinkedList<>();

// 路径:记录在 track 中

// 选择列表:nums 中不存在于 track 的那些元素

// 结束条件:nums 中的元素全都在 track 中出现

private static List> backtrack(int[] nums,List track){

// 结束条件

if (track.size() == nums.length){

result.add(new LinkedList<>(track));

return null;

}

for (int i = 0; i < nums.length; i++) {

if (track.contains(nums[i]))

continue;

// 做选择

track.add(nums[i]);

backtrack(nums,track);

// 撤销选择

track.remove(track.size()-1);

}

return result;

}

}

代码解析:对 {1,2,3} 做全排列,先将 List[0] 放入链表,如果链表中存在该元素,就忽略继续,继续放入List[0+1],同样的,

存在即忽略继续,直到将List中所有元素,无重复的放入链表,这样就完成了一次排列。这个算法的技巧,是利用了链表的

有序性,第一个位置会因为回溯而尝试放入所有的元素,同样,第二个位置也会尝试放入所有的元素。

画出个决策树:

以 {1-3-2} 为例,如果链表第一个位置为1,那第二个位置为 {2,3} 之一,{1}由于属于存在的重复值忽略,

如果第二个位置放了{3},那第三个位置就是{2},就得出了一个结果。

我们对比一下以上两个算法实现: 特别注意,算法B是真正的递归吗?有没有缩小计算规模?

时间复杂度计算公式:分支个数 * 每个分支的计算时间

算法A的分支计算只有元素交换,按Arraylist处理,视为O(1),算法B分支计算包含链表查找为O(N),

算法A:N!* O(1) ,阶乘级别,耗时不送。

算法B:N^n * O(N) ,指数级别,会爆炸!

我使用10个数全排测试如下(严谨的讲,两者有数据结构不同的影响,并不是说仅有算法上的差异):

总结:回溯和递归是两种思想,可以融合,也可以单独使用!

全文完!

我近期其他文章:

1 Redis高级应用

2 聊聊算法——BFS和DFS

3 微服务通信方式——gRPC

4 分布式任务调度系统

5 Dubbo学习系列之十八(Skywalking服务跟踪)

只写原创,敬请关注

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[聊聊算法——回溯算法]http://www.zyiz.net/tech/detail-135184.html

回溯java算法_聊聊算法——回溯算法相关推荐

  1. java 泰勒级数_鸡群优化算法(CSO)、蜻蜓算法(DA)、乌鸦搜索算法(CSA)、泰勒级数(Taylor series)...

    一.鸡群优化算法(Chicken Swarm Optimization, CSO) CSO算法是由Meng等于2014年10月在第五次国际群体智能会议(ICSI)上提出的一种新的仿生算法,CSO算法模 ...

  2. java 哈希一致算法_一致哈希算法Java实现

    一致哈希算法(Consistent Hashing Algorithms)是一个分布式系统中常用的算法.传统的Hash算法当槽位(Slot)增减时,面临所有数据重新部署的问题,而一致哈希算法确可以保证 ...

  3. mysql缓存淘汰机制_聊聊缓存淘汰算法-LRU 实现原理

    前言 我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来.缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据.常用淘 ...

  4. 蚁群算法java实现_简单蚁群算法 + JAVA实现蚁群算法

    一 引言 蚁群算法(ant colony optimization,ACO),又称蚂蚁算法,是一种用来在图中寻找优化路径的机率型技术.它由Marco Dorigo于1992年在他的博士论文中引入,其灵 ...

  5. mysql区间算法_「五大常用算法」一文图解分治算法和思想

    前言 分治算法(divide and conquer)是五大常用算法(分治算法.动态规划算法.贪心算法.回溯法.分治界限法)之一,很多人在平时学习中可能只是知道分治算法,但是可能并没有系统的学习分治算 ...

  6. 人工智能算法_人工智能的灵魂——算法

    人工智能有三驾马车:数据.算法.算力.本文重点介绍算法相关的知识. 本文将介绍算法在人工智能里的概念,算法的4个特征.6个通用方法.以及在选择算法时需要注意的3个点. 什么是算法? 简单的说,算法就是 ...

  7. mysql 编程算法_十大编程算法助程序员走上高手之路

    算法一:快速排序算法 快 速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2)次比较,但这种 状况并不常见.事实上,快速排 ...

  8. hash算法_数据库中间件分片算法之hash

    前言 夜深人静的时候,打开云音乐,点上一曲攀登,带上真无线蓝牙耳机,瞬间燃到爆,键盘打字如飞倦意全无. 分片规则 这几天有人问我,dble和MyCat到底有什么不同.其实dble作为MyCAT的同门, ...

  9. kmeans算法_实战 | KMeans 聚类算法

    1. 写在前面 如果想从事数据挖掘或者机器学习的工作,掌握常用的机器学习算法是非常有必要的,常见的机器学习算法: 监督学习算法:逻辑回归,线性回归,决策树,朴素贝叶斯,K近邻,支持向量机,集成算法Ad ...

  10. 麻雀优化算法_多种智能优化算法应用案例分享-附代码

    1.智能优化算法应用:基于灰狼算法的Otsu图像多阈值分割 智能优化算法应用:基于灰狼算法的Otsu图像多阈值分割-附代码_Jack旭的博客-CSDN博客​blog.csdn.net 2.智能优化算法 ...

最新文章

  1. 界面编程与视图(View)组件
  2. Spring Cloud构建微服务架构:服务容错保护(Hystrix服务降级)
  3. Neverout:一款新的沉浸式益智游戏
  4. android从放弃到精通 第八天 freedom
  5. 线程知识-ThreadLocal使用详解
  6. 用狄拉克函数来构造非光滑函数的光滑近似
  7. WSPBuilder 生成Webpart 部署包
  8. android 日期对话框,Android日期选择器对话框DatePickerDialog使用详解
  9. CentOs6.5 修改主机名
  10. JQuery Dialog(JS模态窗口,可拖拽的DIV)
  11. [VNC] 云服务器 Ubuntu 配置 VNC 遇到的问题
  12. 【李宏毅2020 ML/DL】P85 Transfer Learning
  13. 【STM32 .Net MF开发板学习-03】TinyGUI绘图示例
  14. 高通源代码 Ubuntu14.04下载编译Android(1)
  15. qt中采用G.729A进行网络语音通话实验程序
  16. 5、win7激活秘钥
  17. ubuntu16.04,exFAT格式U盘无法打开解决方式
  18. 日期转换 例如 二零零六年十二月二十一日 转换成 2006年12月21日
  19. java的无极分类,ThinkPHP菜单无极分类实例讲解
  20. PSIM仿真+C语言(c block)实现PID控制和最小拍控制

热门文章

  1. Deis发布1.4版本,支持Microsoft Azure
  2. C++Builder函数集(文件操作、获取时间、类型转换等)
  3. 部署yum仓库自动挂载服务
  4. ubuntu更改mysql的编码配置
  5. linux下查看线程数的几种方法
  6. 850 USB 烧录模式
  7. PowerShell图形化编程2-快速入门
  8. OS存储器管理(一)
  9. Fedora19 搭建LAMP环境
  10. DIY走向高端化:用料成为比拼焦点