一、递归: 函数中调用函数自己,在使用递归的时候一定需要有结束递归的条件,否则就会变成死循环。
想要用递归必须知道两个条件:
1、递归出口(终止递归的条件)
2、递归表达式(规律)
技巧: 在递归中常常是将问题切割成两个部分(1和整体的思想),这能够让我们快速找到递归表达式(规律)
二、递归和循环的区别
简单来说,循环是有去无回,而递归则是有去有回(因为存在终止条件)。
举个栗子,你用你手中的钥匙打开一扇门,结果去发现前方还有一扇门,紧接着你又用钥匙打开了这扇门,然后你又看到一扇们…但是当你开到某扇门时,发现前方是一堵墙无路可走了,你选择原路返回——这就是递归 但是如果你打开一扇门后,同样发现前方也有一扇们,紧接着你又打开下一扇门…但是却一直没有碰到尽头——这就是循环。
三、实例
1、递归案例:求一个数字各个位数上的数字的和
function getEverySum(x) {
if(x<10){
return x;
}
//获取的是这个数字的个位数
return x % 10 + getEverySum( parseInt( x/ 10 ) );
}
console.log(getEverySum(987654321)); // 45
2、平铺多维数组
let arr = [ [1,2,], 3 ,4, [5, [6, [7 ,8, [9]]]]]
let temp = []
function digui(arr) {
for (let i = 0; i< arr.length; i++) {
// 判断是不是数组,若是数组就在进行递归
if (dataType(arr[i]) === 'array') {
digui(arr[i])
} else {
// 不是数组直接push 进去
temp.push(arr[i])
}
}
}
digui(arr)
console.log(temp); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// 判断数据类型
function dataType(obj) {
let o = {}
return o.toString.call(obj).slice(8,-1).toLowerCase()
}
2、求和
// for 求10以内数字的和
var sum=0
for ( var i =0; i < 10 ; i++ ) {
sum = sum + i;
}
console.log(sum ); // 45
// 递归实现n个数字的和
function getSum (x) {
if ( x ==1 ) {
return 1;
}
return x + getSum ( x - 1 );
}
console.log(getSum( 10 - 1 )); // 45
3、获取数组中的最大值
let array = [1, 11,11,115,115];
console.log(getMax(array,0,array.length - 1 )); // 115
/**
* 递归,找出数组最大的值
* @param arr数组
* @param left 左边界,第一个数
* @param right 右边界,数组的长度
* @return
*/
function getMax(arr, left, right) {
// left数组第一个元素
// right赋值为array.length-1
// 如果该数组只有一个数,那么最大的就是该数组第一个值了
if (left === right) {
return arr[left];
} else {
let a = arr[left];
//找出整体的最大值
let b = getMax(arr, left + 1, right);
if (a > b) {
return a;
} else {
return b;
}
}
}
4、汉诺塔玩法
// 汉诺塔的规则
// 有三根柱子,原始装满大小不一的盘子的柱子我们称为A,还有两根空的柱子,我们分别称为B和C(任选)
// 最终的目的就是将A柱子的盘子全部移到C柱子中
// 移动的时候有个规则:一次只能移动一个盘子,小的盘子不能在大的盘子下面(反过来:大的盘子不能在小的盘子上面)
console.log("bar1-- 有三个盘");
towersofHanoi(3, 'bar1', 'bar2', 'bar3');
* 汉诺塔
* @param n n个盘子
* @param start 起始柱子
* @param transfer 中转柱子
* @param target 目标柱子
*
function towersofHanoi( n, start, transfer, target) {
//只有一个盘子,直接搬到目标柱子
if (n === 1) {
console.log(start + "---->" + target);
} else {
//起始柱子借助目标柱子将盘子都移动到中转柱子中(除了最大的盘子)
towersofHanoi(n - 1, start, target, transfer);
console.log(start + "---->" + target);
//中转柱子借助起始柱子将盘子都移动到目标柱子中
hanoi(n - 1, transfer, start, target);
}
}
5、冒泡排序递归写法
冒泡排序:俩俩交换,在第一趟排序中能够将最大值排到最后面,外层循环控制排序趟数,内层循环控制比较次数
以递归的思想来进行改造:
当第一趟排序后,我们可以将数组最后一位(right)和数组前面的数(left,right-1)进行切割,数组前面的数(left,right-1)看成是一个整体,这个整体又是和我们的初始目的(找出最大值,与当前趟数的末尾处交换)是一样的
递归出口:当只有一个元素时,即不用比较了:left === right
let arrays = [2, 3, 4, 5, 1, 5, 2, 9, 5, 6, 8, 3, 1]
bubbleSort(arrays, 0, arrays.length - 1)
console.log(arrays) // [1, 1, 2, 2, 3, 3, 4, 5, 5, 5, 6, 8, 9]
function bubbleSort(arr, left, right ) {
let temp;
//如果只有一个元素了,那什么都不用干
if (left === right) {
} else {
for (let i = left; i < right; i++) {
if (arr[i] > arr[i + 1]) {
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
//第一趟排序后已经将最大值放到数组最后面了
//接下来是排序"整体"的数据了
bubbleSort(arr, left, right - 1);
}
}