递归算法是一种自引用的算法,它通过将大问题分解为更小的相似子问题来解决复杂的计算任务。递归算法的核心思想在于将一个问题分解为一个或多个基本情况和一个或多个规模较小但同样结构的子问题。这些子问题将继续被分解,直到达到基本情况,然后逐层返回结果,最终解决原始问题。
在使用递归算法时,我们需要明确两个关键要素:基本情况和递归关系。
从前有座山山里有座庙,庙里有个小和尚。这个小和尚喜欢讲故事,有一天他开始讲了一个故事:“从前有座山山里有座庙,庙里有个小和尚,小和尚再说一个故事 故事的内容是...”
这个故事似乎永远没有结束的样子。听众们开始思考,这个故事是如何结束的呢?
递归的思想在这个故事中展现得淋漓尽致。小和尚讲的故事不断重复,每次故事的结尾都是开始的部分,形成了一个无限循环的过程。这种无限循环的特性正是递归的本质。
在这个故事中,小和尚讲的故事本身就是一个子问题,而每个子问题又以同样的方式继续展开,不断地迭代下去。
递归算法在开发中有广泛的应用。它可以用来解决各种问题,包括但不限于以下情况:
第五部分:用Java实现递归 下面是一个简单的Java代码示例,用于计算给定数的阶乘:
public class RecursionExample {
public static int factorial(int n) {
// 基本情况:当n为0或1时,直接返回1
if (n == 0 || n == 1) {
return 1;
}
// 递归关系:将问题分解为规模较小的子问题
return n * factorial(n - 1);
}
public static void main(String[] args) {
int number = 5;
int result = factorial(number);
System.out.println("Factorial of " + number + " is: " + result);
}
}
这个比较简单 那我们就继续引入一个较为复杂一点的案例
迷宫问题是一个经典的应用递归思想的例子。它通常描述为在一个二维的迷宫中,从起点到达终点的路径规划问题。现在我们来说明如何通过递归来分析和解决迷宫问题。
我们先把这个迷宫用二维数组画出来:
// 先创建一个二维数组,模拟迷宫
// 地图
int[][] map = new int[8][7];
// 使用1 表示墙
// 上下全部置为1
for (int i = 0; i < 7; i++) {
map[0][i] = 1;
map[7][i] = 1;
}
// 左右全部置为1
for (int i = 0; i < 8; i++) {
map[i][0] = 1;
map[i][6] = 1;
}
//设置挡板, 1 表示
map[3][1] = 1;
map[3][2] = 1;
/**
*
* @param map 表示地图
* @param i 从哪个位置开始找
* @param j
* @return 如果找到通路,就返回true, 否则返回false
*/
public static boolean setWay(int[][] map, int i, int j) {
if(map[6][5] == 2) { // 通路已经找到ok
return true;
} else {
if(map[i][j] == 0) { //如果当前这个点还没有走过
//按照策略 下->右->上->左 走
map[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] != 0 , 可能是 1, 2, 3
return false;
}
}
}
(i, j)
是否为目标位置 (6, 5)
,如果是,说明已经找到通路,返回 true
。map[i][j] == 0
)。如果是可走的,继续执行下面的步骤;否则返回 false
。map[i][j] = 2
)。setWay(map, i+1, j)
) 返回 true
,说明找到了通路,直接返回 true
。setWay(map, i, j+1)
) 返回 true
,说明找到了通路,直接返回 true
。setWay(map, i-1, j)
) 返回 true
,说明找到了通路,直接返回 true
。setWay(map, i, j-1)
) 返回 true
,说明找到了通路,直接返回 true
。map[i][j] = 3
),并返回 false
。map[i][j] != 0
),直接返回 false
。整个算法通过递归的方式,在每个位置上尝试四个方向的移动,直到找到通路或者所有路径都被尝试完毕。如果找到通路,返回 true
,否则返回 false
。在每次递归调用时,都会改变地图的状态,标记已经走过的路径,以及死路。
这个代码只按照了其中一种策略(即下 右 上 左的策略) 这样出来的路径就不一定是最短的 如果需要优化就要用到后面的贪心算法 到时候会专门出一期贪心算法的讲解。
但是这里我们要讲解的是这个递归的思路 可以非常简洁的解决了问题
回溯是一种经典的算法思想,常用于解决在给定的搜索空间中找到所有可能解的问题。它的基本思想是通过尝试不同的选择,当发现当前选择并不是有效的解决方案时,回溯到上一步并尝试其他选择,直到找到所有的解或者确定不存在解。
八皇后问题是一个经典的组合问题,其目标是在一个8×8的棋盘上放置8个皇后,使得任意两个皇后都不能互相攻击,即不能在同一行、同一列或同一对角线上。
解决八皇后问题的思路如下:
arr
,其中 arr[i]
表示第 i
行皇后的列位置。因为每一行只能放置一个皇后,所以解空间可以看作是一个排列问题。
[0, 7]
,表示列的范围。
arr
,将其所有元素初始化为 0
backtrack(arr, 0)
,其中第二个参数表示当前放置的行数。
backtrack
中,首先判断是否已经放置了所有的皇后(即当前行数等于总行数),如果是,则将 arr
添加到结果集中。
arr
中,然后递归调用 backtrack(arr, row + 1)
进行下一行的放置。
arr[row]
的值恢复为 -1,以便尝试其他选择。
public class Queue8 {
static int MaxSize=8;
static int[] arr=new int[MaxSize];
static int count =0;
public static void main(String[] args) {
check(0);
System.out.println("count:"+count);
}
/**
*description<放置第n个皇后>
* @param n 第n个皇后
* @return void
* @author SUZE
* @time 2024/2/18-18:56
*/
public static void check(int n){
if (n==8){//每当n为8意味着已经放置了8个皇后了,因为其实0~7才是需要检验的
printf();
count++;
return;
}
for (int i=0;i<MaxSize;i++){
//先把第n个皇后 放在该行的第i列
arr[n]=i;
if (judge(arr,n)){
check(n+1);//满足了则继续下一位
}
}
}
/**
*description<功能描述>
[arr, i, n] 放置前n个皇后有无冲突
* @return boolean
* @author SUZE
* @time 2024/2/18-18:57
*/
public static boolean judge(int[] arr,int n) {
for (int i=0;i<n;i++){
// 同列皇后情况 两皇后处在斜线的情况 (即纵坐标之差等于横坐标之差 因为可能包含了四个象限所以用绝对值)
if (arr[i]==arr[n]||Math.abs(arr[i]-arr[n])==Math.abs(i-n)){
return false;
}
}
return true;
}
public static void printf(){
for (int i=0;i<MaxSize;i++){
System.out.printf("%d ",arr[i]);
}
System.out.println();
}
}