一、递归的介绍
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=24
System.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;
}
}
//没有同一列以及同一斜线的返回true
return 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