一、介绍
递归通过拆分成各个基本元素,然后再将基本元素组合运算结果作为下一层的中间值。依此向下拆分,然后不断向上组合,直到完成整个运算。
递归算法效率不高,并且代码可读性差,主要应用于数的遍历场景。
二、几种应用
1.斐波拉契数列
1)利用循环实现
num1 + num2 = num3
num2 + num3 = num4
num4 + num5 = num6
…
依此类推
可以总结出来
这里运算几次,就是循环几次,然后每次将两者运算结果作为中间变量。第一个变量等于第二个变量,第二个变量等于中间变量。
循环实现:
int num1 = 1;
int num2 = 1;
int n = 6;
//循环方式
for(int i = 3; i <= n; i++){
int num3 = num1 + num2;
num1 = num2;
num2 = num3;
}
System.out.println(num2);
递归实现:
// 不断递归拆解,然后不断返回
// f(6)分成f(5)+ f(4)
// f(5)分成f(4)+f(3)
// f(4)分成f(3)+f(2)
// f(3)分成f(2)+f(1)
// f(2)返回 1
// f(1)返回1
// f(3)返回 2
// f(2) 返回1
// f(4)返回3
// 接着算f(5)分解出来第二部分f(3)
// f(3) 拆成 f(2) + f(1)
public static int fab(int num){
if(num == 1 || num == 2){
return 1;
}
return fab(num -1) + fab(num -2);
}
2.文件夹遍历
这里应该属于深度遍历。
private static void recursiveFile(File file) {
//获取当前文件夹下所有子文件夹/文件
for (File f : file.listFiles()) {
if (f.isFile()) {
//文件
System.out.println("文件:" + f.getName());
} else {
recursiveFile(f);
}
}
}
3.快速排序
1)介绍
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
2)过程
(1)每次以最左边元素为标杆,然后依次从右到左遍历和从左到右遍历。
(2)当右边遍历过程中发现有比标杆数小的数,就把小的数放到左边遍历到位置,右边停止遍历
(3)然后左边遍历,当发现有比标杆数大的数,就把大的数放到右边遍历到的位置,左边停止遍历
(4)两边遍历直到左右两边遍历到同一个位置结束。然后将标杆数放到这个同一位置上
(5)左边部分重复上面操作,右边部分重复上面操作
3)实现
public class QuickSort {
//快速排序
public static void main(String[] args) {
int[] ints = new int[]{5,3,6,7,9,2,4,8};
sort(ints,0,ints.length -1);
for (int i : ints) {
System.out.println(i);
}
}
//递归左右两边分别进行递归处理
private static void sort(int[] ints, int s, int e){
if(s < e){
int m = sortUnit(ints,s,e);
sort(ints,s,m -1);
sort(ints,m + 1, e);
}
}
private static int sortUnit(int[] ints, int s, int e){
int tmp = ints[s];
int i = s;
int j = e;
// 两边遍历直到同一位置结束遍历
while(i < j){
//右边循环,直到碰到有比标杆数小的数
while(i < j){
if(ints[j] < tmp){
ints[i] = ints[j];
break;
}
j--;
}
//左边循环,知道碰到有比标杆数大的数
while(i < j){
if(ints[i] > tmp){
ints[j] = ints[i];
break;
}
i++;
}
}
//左右碰头的位置,为标杆数左右大小区分的位置
ints[i] = tmp;
return i;
}
}
4)时间复杂度
平均时间复杂为O(nlog2 n),最糟糕时将达到O(n²)的时间复杂度
5)空间复杂度
最优的情况下空间复杂度为O(log2 n),每一次都平分数组;最差的情况下空间复杂度为O( n )
6)稳定性
快速排序每次交换的元素都有可能不是相邻的, 因此它有可能打破原来值为相同的元素之间的顺序.
不稳定
4.八皇后
注意每次进行下层遍历,如果没有找到合适位置,就回来清除该层的皇后位置,尝试下一个位置。并且找到一种方式后,也是清除最后一行的皇后位置,就会倒退不断再次尝试寻找新的摆放方式。
实现:
public class EightQueen {
//八皇后问题
//八行八列的格子里面,放上一个皇后后,上下左右,左上右上,左下右下都能再放皇后
//如果这八行都放上皇后,该是什么排列
//初始化一个八行八列的格子图,,0表示没有皇后,1表示放了皇后
static int[][] map = new int[8][8];
public static void main(String[] args) {
play(0);
}
//从某一行开始开始摆放皇后
private static void play(int row){
//遍历该行的每个列
for(int i = 0; i < 8; i++){
// 判断是否有位置可以放皇后
if(check(row,i)){
//该位置可以放皇后
map[row][i] = 1;
if(row == 7){
//如果最后一行都放置了皇后,打印摆列方式
show();
//这里寻找下一种摆法,只需要把最后一行的皇后清除,这时就会最后一行就会依次尝试摆放皇后,没有找到,就会清除上一层皇后,然后类似清除,尝试,直到找到新的一种摆放方式。
// map[row][i] = 0;
}else{
//没有到达最后一行,就继续处理下一行
play(row + 1);
// 如果row+1行中没有可放皇后的位置,就回到这一步,清除row中之前皇后的位置,让循环继续下去,尝试下一个位置时,row+1是否可以摆放。
// map[row][i] = 0;//去除皇后
}
map[row][i] = 0;
}
}
}
//展示棋盘落子
static int count = 1;
private static void show() {
System.out.println("第"+count+"种摆法");
count++;
for(int i = 0; i < 8; i++){
for(int j = 0; j < 8; j++){
System.out.print(map[i][j] + " ");
}
System.out.println();
}
}
//因为是从上往下放,所以当放到第row行时,row下面的肯定没有放,左右也不会放,所以只需要考虑上面三个方向
private static boolean check(int row, int col){
//检查上面方向(行数减少,列数不会变)
for(int i = row -1; i >= 0; i--){
if(map[i][col] == 1){
return false;
}
}
//检查左上方向(行数减少,同时列数也减少)
for(int i = row -1, j = col -1; i >=0 && j >= 0; j-- , i--){
if(map[i][j] == 1){
return false;
}
}
//检查右上方向(行数减少,同时列数增加)
for(int i = row -1, j = col + 1; i >= 0 && j < 8; i--,j++){
if(map[i][j] == 1){
return false;
}
}
return true;
}
}