递归函数都有两部分: 基线 条件(base case)和递归条件(recursive case)
递归条件指的是函数调用自己,而基线条件则 指的是函数不再调用自己,从而避免形成无限循环。
1. sum
O(n)
var list = [2,4,56,32];
function sum(list){
if(list.length){
return list.pop() + sum(list);
}else{
return 0;
}
}
var a = sum(list);
2. max
O(n)
var list = [2,4,56,32,90];
function maxNo(list,max=0){
if(list.length){
let curNo = list.pop();
curNo = curNo > max ? curNo : max;
return maxNo(list,curNo);
}else{
return max;
}
}
var a = maxNo(list);
3. quick-sort
O(nlogn)
D&C基本步骤(divide and conquer,D&C)
(1) 找出基线条件,这种条件必须尽可能简单。
(2) 不断将问题分解(或者说缩小规模),直到符合基线条件。
var list = [9,4,56,32,90,2,3];
function quickSort(list){
if(list.length){
const inite = list.shift();
const small = [];
const great = [];
list.map( item => {
item < inite ? small.push(item) : great.push(item);
});
return [...quickSort(small), inite, ...quickSort(great)];
}else{
return list;
}
}
var a = quickSort(list);
柯里化 尾递归
// 柯里化函数
function curry (fn) {
var _fnArgLength = fn.length;
function wrap (...args) {
var _args = args; // [10]
var _argLength = _args.length;
// 如果传的是所有参数,直接返回fn调用 10,1,0
if (_fnArgLength === _argLength) {
return fn.apply(null, args);
}
function act (...args) {
_args = _args.concat(args); // [1]
if (_args.length === _fnArgLength) {
// 递归结束
return fn.apply(null, _args); // 10,1,0
}
return act; // ...递归
}
return act; // 2. fn(10)
}
return wrap; // 1. init fn
}
// 尾递归函数
function tailFactorial (num, total) {
if (num === 1) return total;
return tailFactorial(num - 1, num * total);
}
// 改写
var factorial = curry(tailFactorial); // return wrap;
var a = factorial(10); // return act;
a(1); // act(1);
// 3628800;
// 等于
factorial(10,1)
// 尾递归函数
function tailFactorial (num, total,inite) {
if (num === 1) return total+':'+inite;
return tailFactorial(num - 1, num * total,inite+1);
}
factorial(10)(1)(2) // "3628800:11"
factorial(10,1,0)
// 柯里化
function curry(targetfn) {
var numOfArgs = targetfn.length;
return function fn(...rest) {
if (rest.length < numOfArgs) {
return fn.bind(null, ...rest);
} else {
return targetfn.apply(null, rest);
}
};
}