一、递归的介绍
1、递归应用场景

2、递归的概念

3、递归调用机制(案例+图示)
案例介绍

package com.mjj.test;public class UsualExer {//打印问题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;}return factorial(n-1)*n;}public static void main(String[] args) {//测试打印问题System.out.println("测试打印问题:");test(4);System.out.println();//测试阶乘问题 1*2*3*4=24System.out.println("4的阶乘处理结果为:"+factorial(4));}}
运行结果:
图解说明
函数调用使用到的机制是栈,每次先执行栈顶的事件。
4、递归能解决什么样的问题

5、递归需要遵守的重要规则

二、递归-迷宫问题
源代码:MazeExer.java
题目说明
有8行7列的二维数组,中间绿色的是围墙,从(1,1)开始,终点为(6,5),在对应走的路径上设置为2表示走通。
代码演示
说明:走到(6,5)位置即为走通迷宫,对应数组位置上坐标0代表没走过,1代表是围墙,2代表走过,3表示已走过没有路
package Recursion;public class MazeExer {//将二维数组打印public static void printArr(int arr[][]) {for(int i=0;i<arr.length;i++) {for(int j=0;j<arr[i].length;j++) {System.out.print(arr[i][j]+" ");}System.out.println();}}//进行迷宫/**** @Description* @auther changlu* @date Aug 12, 202011:55:22 AM* @param arr 使用二维数组作为迷宫* @param i 是当前位置的y轴* @param j 是当前位置的x轴* @return 返回true或false 代表该路是否可以走通*/public static boolean setWay(int arr[][],int i,int j) {//坐标0代表没走过,1代表是围墙,2代表走过,3表示已走过没有路//设置(1,1)起点,(6,5)为终点if(arr[6][5] == 2) {return true;}else {//当前位置为0时if(arr[i][j] == 0) {arr[i][j] = 2;//表示已经走过//可以往4个方向走if(setWay(arr, i+1, j)) {//这里往下走return true;}else if(setWay(arr, i, j+1)) {//这里往右走return true;}else if(setWay(arr, i-1, j)) {//往上走return true;}else if(setWay(arr, i, j-1)) {//往左走return true;}else {//上下左右路都走不通arr[i][j] = 3;//标识这里已经走过并且走不通return false;}}else {//当前位置上为1,2,3 就都不走return false;}}}}
测试:
public class MazeExer {public static void main(String[] args) {//创建一个8行7列的迷宫int[][] arr = new int[8][7];//给迷宫外围一圈赋值围墙for(int i=0;i<=6;i++) {arr[0][i] = 1;arr[7][i] = 1;}for(int i=0;i<=7;i++) {arr[i][0] = 1;arr[i][6] = 1;}//额外作墙板arr[3][1] = 1;arr[3][2] = 1;//第一次打印printArr(arr);boolean flag = setWay(arr, 1, 1);System.out.println("是否能走通:"+flag);//打印迷宫System.out.println("\n走通后的迷宫:");printArr(arr);}}
运行结果:
三、递归-八皇后问题(回溯算法)
1、八皇后问题介绍


2、八皇后问题算法思路分析


代码演示(说明)
属性说明:这里棋盘是否一个一维数组来进行代替,如arr[0] = 2,这个下标0代表是第一行,2代表的是这行上的第三个,使用count来计算方法的总数。下标为行数,值为行中的位置。
方法说明:这里总共 三个方法,打印数组、检查对应行与之前的棋盘有同列以及同斜线的、递归回溯求解一维数组。
printArr():使用Arrays打印数组
check(int n) :传入下标,通过一维数组来查看是否不符合同列以及同斜线情况,符合返回true,不符合返回false
getQueue8(int n):通过递归来对棋盘上所有情况进行检查选择,中途会使用check()进行检查
时间复杂度为:8^8,整体算法会有回溯,从而暴力解决获取所有情况。
package StackExer;import java.util.Arrays;public class Queue8 {//使用一维数组来代替八皇后棋盘//下标为第几行,对应的值这一行的第几个private int max = 8;private int [] array = new int[8];private static int count;//进行八皇后棋盘获取,n为行public void getQueue8(int n) {//如果n为8说明,已经成功获取八皇后棋盘摆棋方式if(n == max) {printArr();return;}//进行遍历for(int i=0;i<max;i++) {//填充第n列上的值为i,i代表第几个位置array[n] = i;//判断是否有出现冲突的情况if(check(n)) {//没有冲突情况进行递归getQueue8(n+1);}}}//创建一个检查方法,传入的n是第几行public boolean check(int n) {//判断是否存在同一列以及同一斜线的for(int i=0;i<n;i++) {if(array[i] == array[n] || Math.abs(n-i) == Math.abs(array[i]-array[n])) {return false;}}//没有同一列以及同一斜线的返回truereturn true;}//用来打印一维数组public void printArr() {count++;System.out.println(Arrays.toString(array));}}
测试方法:
public class Queue8 {public static void main(String[] args) {Queue8 queue8 = new Queue8();queue8.getQueue8(0);System.out.printf("摆棋方式有%d种",count);}}
运行结果:
四、全排列
参考文章:轻松理解全排列算法的递归解法
**
详细过程(二部分)
① 入门,并不是求解全排列,而是列举四种全排列形式

public class Main {public static void main(String[] args) {int[] a = {1,2,3,4};quanpailie(a,0,a.length);}private static void quanpailie(int[] a, int cur, int length) {for(int i = cur;i<length;i++){swap(a, cur,i );System.out.println(Arrays.toString(a));swap(a, i, cur);//将先前替换的撤销}}public static void swap(int[]a,int cur,int i){int temp = a[cur];a[cur] = a[i];a[i] = temp;}}
运行结果:
② 使用递归求解全排列
添加出口条件,来进行输出
package exer;import java.util.*;public class Main {public static void main(String[] args) {int[] a = {1,2,3,4};quanpailie(a,0,a.length);}//进行全排列private static void quanpailie(int[] a, int cur, int length) {//输出列举的情况if(cur == length){System.out.println(Arrays.toString(a));}for(int i = cur;i<length;i++){swap(a, cur,i );//System.out.println(Arrays.toString(a));quanpailie(a, cur+1, length);//分解为子问题进行递归swap(a, i, cur);}}//替换操作public static void swap(int[]a,int cur,int i){int temp = a[cur];a[cur] = a[i];a[i] = temp;}}
包含24种情况
—————————————————————————————————————————————————————————————
**
解决全排列重复问题(忽略重复的全排列)
对于全排列如果我们不想要重复的排列数据时,如下数组中包含重复的元素:
会出现重复的情况
**
添加一个查询方法:
cur是数组当前从cur位置开始,i是对应要调换的数,我们只需要判断在[cur,i)之间是否与在数组中i位置的数据相同,那么我们就返回false
//判断是否出现重复的情况public static boolean isRepeat(int[] b,int cur,int i){//因为我们是要将cur与i来进行调换,如果之前重复的话就不需要进行往下递归来生成对应值for(int j=cur;j<i;j++){if(b[j] == b[i])return false;}return true;}
完整如下:
public class Main {public static void main(String[] args) {int[] a = {1,2,2,3,4};quanpailie(a,0,a.length);}//进行全排列private static void quanpailie(int[] a, int cur, int length) {//输出列举的情况if(cur == length){System.out.println(Arrays.toString(a));}for(int i = cur;i<length;i++){//判断是否与之前的有重复元素的情况if(!isRepeat(a, cur, i))return;swap(a, cur,i );//System.out.println(Arrays.toString(a));quanpailie(a, cur+1, length);//分解为子问题进行递归swap(a, i, cur);}}//替换操作public static void swap(int[]a,int cur,int i){int temp = a[cur];a[cur] = a[i];a[i] = temp;}//判断是否出现重复的情况public static boolean isRepeat(int[] a,int cur,int i){//因为我们是要将cur与i来进行调换,如果之前重复的话就不需要进行往下递归来生成对应值for(int j=cur;j<i;j++){if(a[j] == a[i])return false;}return true;}}
就没有重复情况了
整理者:长路 时间:2020/8/12、2020/11/13
