您的位置:首页 > 其它

不使用 for , while ,递归,如何解决一道简单的算法题?

2017-06-27 20:01 337 查看
今天碰到一道关于的斐波那契数列的题

首先还是要认识一下这个「 斐波那契数列 」,我最早接触都这个数列是在高中的时候。当时在学校,我属于喜欢看课外书的学渣,不知道哪一天就接触到一本 C++ 编程入门书,然后里面就有一节就是如何实现斐波那契数列……还是先看题吧:

题目

给定一个参数数组 arr 和一个要返回的数组的长度 n, 使用一个函数来求出类似斐波那契数列所组成的数组

例子:

tribonacci([1, 1, 1] , 5)  // [1,1,1,3,5]
tribonacci([1, 1, 1], 10)  // [1,1,1,3,5,9,17,31,57,105]


原题链接:https://www.codewars.com/kata/tribonacci-sequence/train/javascript

思路

题目的关键:根据例子可以得知,数组中三个相邻的元素的和是数组中新的元素,这个新元素的位置是三个求和元素中的最后一个元素的索引 + 1, 问题来了如何获得数组中对应的相邻的三个元素?

这种类似的题其实解法挺多的,编程嘛,就要挑战一下新思路,若不使用 for , while 以及 递归的手法,如何实现遍历,并解决这个问题。我使用的手法:

在贴代码之前我先复习一下在 JavaScript 中可以用哪些方法实现数组的遍历

使用 for 或 while

数组提供的遍历的函数

还有就是递归

解法

个人比较喜欢函数所以这次就使用第二种方法:使用数组中的方法

function tribonacci(signature,n){

// 处理遍历的元素,得到一个新的元素
let handleThree = (index) => {

// 前数组三项永远都是不改变,是传进来的三项
if (index < signature.length) {
return signature[index];
} else {

// 过滤出数组中对应的三项元素,求和并更新传进来的数组,
let addItem = signature
.filter((item, signIndex) => (index - 3) <= signIndex)
.reduce((itemB, itemA) => itemB + itemA)

signature.push(addItem);
return addItem;
}
}

// 得到最后的结果
return Array.apply(null, {length: Number(n)})
.map((item, index) => handleThree(index));
}


具体的代码实现感觉写的有点冗长,不过逻辑还是很明了

整个大的函数返回值是一个数组。一个用 Array 创建的新数组使用 apply 把数组的长度确定,然后用 map 进行遍历,遍历的时候对在 map 中对每一项元素进行一些操作:

首先是判断是不是前三项

是前三项直接出来返回对应的值

若不是前三项则开始获取到需要相加的三项元素,我用了 filter 来拿到,之后用 reduce 来进行求和操作(这一步很关键,此时我已经拿到来一项新的值),接着更新传进来的数组,之后返回到 map 中进行下一个操作;直到遍历完整个数组。

在这个题中使用 apply 的一个特殊用法,把一个对象传到 Array 构造函数中,这个对象就包含这这个数组的长度,这个用法并不常见。

apply

很多人可能知道 apply , call 在扩充函数作用域的情况之下他们的异同之处。其实 apply 还有一个特殊的用处,就是用来传递特殊的值

例子一:求数组中最大值

let arr = [1, 2, 3, 4, 5, 6];

Math.max(num); // NaN

// 方法一 es6之后的写法
Math.max(...num); // 6

// 方法二 es6之前的写法
Math.max.apply(null, num); // 6


方法一属于 es6 的 「 扩展语法 」的用法

这个例子中很清楚得知 Math.max() 里面的参数为数组的时候,并且不做处理,得到不是我们想要的结果

例子二:使用 apply 填充数组

// 方法一
Array.apply(null, ['5']); // ['5']

// 方法二
Array.apply(null, [5]); // [undefined x 5]

// 方法三 得到的结果等价于 方法二
Array(5); // [undefined x 5]

// 方法四
Array.apply(null, {length: 5}); // [undefined, undefined, undefined, undefined, undefined]


上面的写法不一样,但是做的事情是一样的:通过构造函数 Array 创建了一个数组,其中有些写法很糟糕,比如:方法一,方法二,方法三(没有人会这么创建一个数组,更多是采用字面量的方式)。值的注意的地方是方法二中的写法,创建的其实是这样的数组:

// 方法二
Array.apply(null, [5]); // [, , , , ,] 或 [, , , , , ,]


并且有这样的问题,调用 map 或 forEach 的时候不会执行:

let arr = Array.apply(null, [5]);
arr.forEach((index) => index); //不会执行


所以方法四的优势就显现出来,可以通过 map 或 forEach 进行遍历整个数组

let arr = Array.apply(null, {length: 5}); // [undefined, undefined, undefined, undefined, undefined]
arr.map((el, index) => index); //[0, 1, 2, 3, 4]


省略 new 的写法参考 《 JavaScript 高级程序设计(第3版) 》第5章-引用类型 5.2-Array 类型(86页)

这也就是我的解答中采用这个方法的原因:能够确定数组的长度,并且内部的元素不确定;但想使用函数的方式进行遍历,恰好有这样的一种方式可以实现。

欢迎交流新的解题思路

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐