数据结构和算法第六章递归

数据结构和算法第六章递归


文章目录

  • 数据结构和算法第六章递归
  • 前言
  • 一、递归与回溯?
  • 二、递归用于解决的问题
    • 1.打印问题和阶乘问题回顾递归调用机制
    • 2.递归--迷宫问题(小球找路问题)
    • 3.递归--八皇后问题
  • 小练习(面试题 08.06. 汉诺塔问题)

前言

数据结构和算法第六章递归


一、递归与回溯?

一、递归与回溯概念的理解
1)程序调用自身的编程技巧称为递归( recursion):
递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为
一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
2)回溯思路:
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,
这种走不通就退回再走的技术为回溯法。

回溯的思路基本如下:当前局面下,我们有若干种选择,所以我们对每一种选择进行尝试。如果发现某种选择违反了某些限定条件,此时 return;
如果尝试某种选择到了最后,发现该选择是正确解,那么就将其加入到解集中。
在这种思想下,我们需要清晰的找出三个要素:选择 (Options),限制 (Restraints),结束条件 (Termination)。
3)用一个比较通俗的说法来解释递归和回溯:

我们在路上走着,前面是一个多岔路口,因为我们并不知道应该走哪条路,所以我们需要尝试。尝试的过程就是一个函数。
我们选择了一个方向,后来发现又有一个多岔路口,这时候又需要进行一次选择。所以我们需要在上一次尝试结果的基础上,
再做一次尝试,即在函数内部再调用一次函数,这就是【递归】的过程。
这样重复了若干次之后,发现这次选择的这条路走不通,这时候我们知道我们上一个路口选错了,所以我们要回到上一个路口重新选择其他路,这就是【回溯】的思想。

二、递归需要遵守的重要规则:
1) 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
*
递归的底层用到了栈,当程序执行到一个方法时,就会开辟一个独立的空间(栈空间),它是独立的,好像把每个空间放到一个大的栈里面去,每次执行的时候,是把新的栈压在栈顶,当然执行也是从上面开始执行的,执行完了回到下一个栈,最后回到栈低,main栈的时候,程序结束,递归的底层、递归编译器的底层用到了栈的机制
*
2) 方法的局部变量是独立的,不会相互影响, 比如 n 变量
3) 如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据,(迷宫问题,让多个栈共享同一个数组,不管调用产生多少个栈,都是修改同一张
地图)
4) 递归必须向退出递归的条件逼近,否则就是无限递归,出现 StackOverflowError,死递归了:(比如打印问题中test(n-1))
不停的递归,出现栈溢出(StackOverflowError)
5) 当一个方法执行完毕,或者遇到 return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕

二、递归用于解决的问题

  1. 各种数学问题如: 8皇后问题 , 汉诺塔, 阶乘问题, 迷宫问题, 球和篮子的问题(google 编程大赛)
  2. 各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等.
  3. 将用栈解决的问题–>递归代码比较简洁

1.打印问题和阶乘问题回顾递归调用机制

递归的调用机制:通过打印问题,回顾递归调用机制
递归的调用规则:
1,当程序执行到一个方法时,就会开辟一个独立的空间(栈帧)
2,每个空间的数据(局部变量),是独立的(比如图解中:每个栈帧都有一个n变量,互不影响)
3,递归的一个方法执行完毕,或者遇到ruturn,就会返回到调用该方法的地方
图解:打印问题回顾递归调用机制.png

/*
打印问题
*/
public static void test(int n){if(n>2){test(n-1);}System.out.println("n="+n);}/*阶乘问题*/public static int factorial(int n){if(n==1){return 1;}else{return factorial(n-1)*n;}}

2.递归–迷宫问题(小球找路问题)

public static void main(String[] args) {//创建二维数组,模拟迷宫,使用1为墙,上下置为1,左右置为1,设置挡板为1//创建一个8行7列的二维数组int[][] map = new int[8][7];//将上下置为1for (int i = 0; i < 7; i++) {//将第一行置为1,最后一行置为1,做墙map[0][i] = 1;map[7][i] = 1;}//将左右置为1for (int i = 0; i < 8; i++) {//将第一列和最后一列置为1map[i][0] = 1;map[i][6] = 1;}//设置挡板map[3][1] = 1;map[3][2] = 1;//输出地图System.out.println("迷宫如下:");for (int i = 0; i < 8; i++) {for (int j = 0; j < 7; j++) {System.out.print(map[i][j] + " ");}System.out.println();}//小球找路,小球的起始位置在(1,1)位置setWay(map, 1, 1);//输出小球找路的路径System.out.println("小球找路的路径如下图所示:");for (int i = 0; i < 8; i++) {for (int j = 0; j < 7; j++) {System.out.print(map[i][j] + " ");}System.out.println();}}/*** @param map 地图* @param i   小球起始位置所在的行* @param j   小球起始位置所在的列* @return 如果找到通路,就返回true,否则返回false* 通路即为小球能到map[6][5]位置,就说明通路找到* 约定:当map[i][j]==0表示该点还没有走过* ==1表示墙* ==2表示通路可以走* ==3表示该点已经走过,但是走不通* 走迷宫时,需要确定一个策略(方法)下-->右-->上-->左*///小球找路,将地图,与起始位置传过来public static boolean setWay(int[][] map, int i, int j) {//判断如果直接map[6][5]为2时,表示该路可走,通路已经找到,直接返回trueif (map[6][5] == 2) {return true;} else {if (map[i][j] == 0) {//如果该点换没走过//如果该点还没走过,就按照上面规定的策略即方法走//先假定该点可以走通,将能走通的点标记为2,下次回溯就可以不用再走啦,要不然回溯太多容易出现栈溢出StackOverflowmap[i][j]=2;if (setWay(map, i + 1, j)) {//先向下走return true;} else if (setWay(map, i, j + 1)) {//再向右走return true;} else if (setWay(map, i - 1, j)) {//再向上走return true;} else if (setWay(map, i, j - 1)) {//再向左走return true;}else{//说明该点走不通,是死路map[i][j]=3;return false;}} else {//该点已经走过,即就是当map[i][j]=1,2,3这三种情况表示都已经走过,不需要再走,直接返回falsereturn false;}}}

3.递归–八皇后问题

八皇后问题:任意两个皇后都不能处于同一行、 同一列或同一斜线上,问有多少种摆法(92种)
八皇后问题算法思路分析:

  1. 第一个皇后先放第一行第一列
  2. 第二个皇后放在第二行第一列(编写一个方法,放第n个皇后)、然后判断是否 OK(查看我们放置第n个皇后,就去检测该皇后是否和前面已经摆放的皇后冲突), 如果
    不 OK,继续放在第二列、第三列、依次把所有列都放完,找到一个合适
  3. 继续第三个皇后,还是第一列、第二列……直到第 8 个皇后也能放在一个不冲突的位置,算是找到了一个正确解
  4. 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解,全部得到.
  5. 然后回头继续第一个皇后放第二列,后面继续循环执行 1,2,3,4 的步骤
  6. 示意图:递归八皇后问题图解
     说明: 理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法,用一个一维数组即可解决问题. arr[8] = {0, 4, 7, 5, 2, 6, 1, 3}
    //对应 arr 下标 表示第几行,即第几个皇后,{arr[i] = val , val 表示第 i+1 个皇后,放在第 i+1 行的第 val+1 列}

    //先定义一个max来表示有几个皇后int max=8;//定义一个计数变量来统计打印了多少次,即就是有多少种解法static int count=0;//定义一个计数变量judgeCount来统计一共判断了多少次,static int judgeCount=0;//创建一个一维数组,arr[i] = val , val 表示第 i+1 个皇后,放在第 i+1 行的第 val+1 列//理论上应创建一个二维数组来表示棋盘,但其实可用一个一维数组来解决问题int[] array=new int[max];public static void main(String[] args) {//创建对象调用方法递归八皇后问题 queue8 =new 递归八皇后问题();queue8.check(0);System.out.println("8皇后的解法一共有:"+count+"种解法");System.out.println("一共判断冲突的次数为%d:"+judgeCount+"次");}//编写一个方法来放第n个皇后void check(int n){//如果n为8,就表示8个皇后已经放好,就将其打印,并退出if(n==8){print();count++;return;}//依次放入皇后,并判断是否冲突for(int i=0;i<max;i++){//先将当前这个皇后n放在第一列array[n]=i;//判断第n个皇后放在第i列时是否冲突if(judge(n)){//不冲突时//接着放第n+1个皇后,即开始递归check(n+1);}//如果冲突的话,就继续执行array[n]=i;将第n个皇后放在第i+1列,即本行后移的一个位置}}//查看我们放置第n个皇后,就去检测该皇后是否和前面已经摆放的皇后冲突private boolean judge(int n){judgeCount++;for(int i=0;i<n;i++){//由于每次放第n个皇后其行都在加1,故不需要判断其是否在同一行//arr[i]==arr[n]表示第n个皇后和第i个皇后在同一行//|arr[n]-arr[i]|=(n-i)表示第n个皇后和第i个皇后在同一斜线上if(Math.abs(array[n]-array[i])==Math.abs(n-i)||array[i]==array[n]){return false;}}return true;}//写一个方法可以将皇后的摆放的位置输出private void print(){for(int i=0;i<array.length;i++){System.out.print(array[i]+" ");}System.out.println();}

小练习(面试题 08.06. 汉诺塔问题)

https://leetcode-cn.com/problems/hanota-lcci/

数据结构和算法(第六章递归)相关推荐

  1. [翻译]C#数据结构与算法 – 第六章BitArray类

    BitArray类用于在资源有限的情况下表示一系列比特值.比特集合可以存储于常规数组,但是如果我们使用专为比特集合设计的数据结构则可以创建更高效的程序.在本章中,我们将学习一下怎样使用这种数据结构并研 ...

  2. 数据结构与算法(六)

    数据结构与算法 第一章 绪论 第二章 线性表 第三章 树与二叉树 第四章 图 第五章 查找 第六章 排序 文章目录 数据结构与算法 第六章 内部排序 一.基本概念 二.冒泡排序 三.快速排序 四.直接 ...

  3. 数据结构与算法(六)- 单向链表的反转

    数据结构与算法(六)- 单向链表的反转 一.头节点插入法 /*** 反转单向链表(头插法)** 1.先定义一个节点reverseHead = new HeroNode()* 2.从头到尾遍历原来的链表 ...

  4. 06_JavaScript数据结构与算法(六)单向链表

    JavaScript 数据结构与算法(六)单向链表 认识链表 链表和数组 链表和数组一样,可以用于存储一系列的元素,但是链表和数组的实现机制完全不同. 数组 存储多个元素,数组(或列表)可能是最常用的 ...

  5. 比特数据结构与算法(第二章收尾)带头双向循环链表的实现

    1.链表的分类 链表的分类 ① 单向或者双向 ② 带头或者不带头 ③ 循环或者非循环 常用的链表: 根据上面的分类我们可以细分出8种不同类型的链表,这么多链表我们一个个讲解这并没有意义.我们实际中最常 ...

  6. 为什么我要放弃javaScript数据结构与算法(第二章)—— 数组

    第二章 数组 几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构.JavaScript里也有数组类型,虽然它的第一个版本并没有支持数组.本章将深入学习数组数据结构和它的能力. 为什么 ...

  7. 【数据结构总结】第六章 图(非线性结构)

    第六章 图(非线性结构) 提示:本文主要是以思维导图的形式概括数据结构第一章的精华内容,基本不会用到文字性的内容,目的是为了给大家梳理每个重要的知识点的相关概念,方便大家在复盘的时候快速阅读和浏览,加 ...

  8. 读《学习JavaScript数据结构与算法》 第二章

    第二章 ECMAScript和TypeScript概述 文章目录 第二章 ECMAScript和TypeScript概述 前言 一.let const 二.模板字面量 支持换行 拼接方式 三.函数的默 ...

  9. 【离散数学中的数据结构与算法】六 排列与组合二

    接着上一篇学习:[离散数学中的数据结构与算法]五 排列与组合一 上一篇文章主要学习了可重复选取的可重排列和不可重复选取的排列.他们都是在n个不同的对象中选取. 今天我们俩学习的是,当这个n个对象中有相 ...

最新文章

  1. 接口入参形式_某小公司RESTful、共用接口、前后端分离、接口约定的实践
  2. windows10如何下载和安装latex
  3. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十三║Vue实战:Vuex 其实很简单
  4. asp.net(mvc) 框架
  5. 通用的权限管理系统发布
  6. Js中fetch方法
  7. 在搜索框自动输入文本_selenium自动化测试之常见的定位元素
  8. Html5不允许修改但允许赋值,JavaScript | 引用类型变量的赋值问题
  9. 手机上怎么制作电子版证件照
  10. JAVA实现经纬度转换小数的工具类
  11. K3S配置IPV6环境
  12. html5中表格怎么写,HTML5制作表格样式本
  13. 【CSP-S2019模拟】09.25比赛总结
  14. 一个好的学习算法的网站
  15. 腾讯员工的1则匿名帖子,让我细思极恐:不要低估人性的恶
  16. 相对定位的元素会在原先的地方
  17. 数据结构—约瑟夫环问题(C语言版)
  18. Mac下github的基本使用(有详细过程)
  19. 成功解决OBS中的回音问题
  20. 安卓android BMI体质指数测试项目

热门文章

  1. 获取系统时间以及时间戳的理解与使用
  2. 移动硬盘上安装WIN PE最简单的方法http://dzh.mop.com/topic/main/readSubMain_6459066_0.html
  3. 《神经质人格》摘录(第十三章)
  4. js获取当天最后一秒和所在星期最后一秒的时间戳
  5. 捞财宝等上海互金协会会员主动发布网贷行业自律声明
  6. word插件php,php使用phpword插件生成word文档
  7. Html中空格转义字符
  8. 如何在WPS中显示NoteExpress插件
  9. Android面试心得,已拿到字节跳动,京东,android实战项目源码
  10. Django QuerySet API 文档阅读(3):QuerySet定义(一)