Codewars刷题思路分享(JS)-----11~20

466 阅读9分钟

“有些事情本来很遥远,你争取,它就会离你越来越近”

11. 猫和架子(Cats and shelves)

描述

无数个货架以交错的方式排列在另一个之上。
根据插图,猫可以同时跳到 3 个架子:从架子 1 跳到架子 2 或 4(猫不能爬到头顶正上方的架子上):

                 ┌────────┐
                 │-6------│
                 └────────┘
┌────────┐       
│------5-│        
└────────┘  ┌─────► OK!
            │    ┌────────┐
            │    │-4------│
            │    └────────┘
┌────────┐  │
│------3-│  │     
BANG!────┘  ├─────► OK! 
  ▲  |_/|   │    ┌────────┐
  │ ("^-^)  │    │-2------│
  │ )   (   │    └────────┘
┌─┴─┴───┴┬──┘
│------1-│
└────────┘

输入:

开始和结束货架编号(始终为正整数,完成时间不小于开始)

任务

找到从开始到结束的最小跳跃次数

思路

首先考虑开始(start)和结束(finish)相同的情况,此时次数为0。 当不相同时,定义一个 now 记录猫现在所在的货架编号,定义一个 sum 记录跳跃次数。再用 while 循环,当 结束货架和现在所在货架的差大于等于3时,继续循环,每次循环中,猫跳三格,跳跃次数加一,直到不满足,此时有三种可能,差为0,猫不用再跳;差为1,猫跳一次;差为2,猫跳两次。最后相加就行。

代码

function solution(start, finish) 
{
    if(start === finish) {
        return 0;
    }
    let now = start;
    let sum = 0;
    while((finish - now) >= 3) {    //还可以每次跳三格
        now += 3;
        sum ++;
    }
   return sum + (finish - now) % 3;
}

优化思路

在声明中再定义一个参数 difference,值为 finish - start, 只要 difference >= 3,猫就会跳三格,可以直接用 difference 除以 3, 再向下取整,就可以得到猫需要跳三格的次数,剩下的difference 只有三种可能----0, 1, 2,和前面思路一样对 3 求余就行,再把两个相加。

优化代码

const solution = (start, finish, difference = finish - start) => Math.floor(difference / 3) + difference % 3;

12. 下一个回文数( Next Palindromic Number)

描述

CW 中关于回文数和回文弦的问题过去和现在都存在。(设n是一任意自然数。若将n的各位数字反向排列所得自然数n1与n相等,则称n为一回文数。)

在本题中,你会得到一个正整数,你必须创建一个函数(Javascript),它将输出高于的最小回文数。

思路

首先应该要得到每个数的各位数反向排列的数,要颠倒首先想到数组的 reverse() 方法,可以先用 toString() 方法将数字类型变为字符串类型,再用 split("") 方法将字符串类型变为数组类型, 再用数组的 reverse() 方法将数组颠倒,再用 join() 方法将颠倒的数组变为字符串类型,再用Number() 方法将字符串类型变回数字类型。这样就得到了颠倒之后的数。
直接进入do while 循环,每次循环让输入的数 val ++,再得到它的颠倒之后的数,循环判断 这个数跟他的颠倒数是否相等,若不相等继续循环,若相等,结束循环,这个数就是回文数。

代码

const nextPal = (val) => {
    do 
    {
        val ++;
        var str = val.toString();
        var arr = str.split("");
        var num = Number(arr.reverse().join("")); 
    } while(num !== val)
    return val;
}

13. 寻找奇数(Find the odd int)

描述

给定一个整数数组,找到出现奇数次的整数。

始终只有一个整数出现奇数次。

示例

输入:[0] 输出: 0

输入:[1, 1, 2] 输出: 2

输入:[0, 1, 0, 1, 0] 输出: 0

输入:[1, 2, 2, 2, 4, 2, 1] 输出: 4

思路

先用 sort() 方法给数组排序,声明变量 sum 记录每个元素出现的次数,sum 初始值为1, 声明 char 记录每个元素,char 初始值为数组的第一个元素,for 循环遍历数组,每一次循环中,判断相邻元素是否相等,若不相等,先判断 sum 是否为奇数(奇数可用对2求余来判断),如果是,则 return char,否则将下一项的数组元素存到 char 中,并将 sum 重置为1; 若相邻元素相等,则令 sum 加一。这时候还有个小bug,就是如果数组的最后一个元素出现次数是奇数,前面的元素出现次数都是偶数,则不会返回,故在 for 循环结束后,如果还没有任何返回,则在函数的最后 返回数组的最后一项。

代码

function findOdd(A) {
    let arr = A.sort();
    let sum = 1;
    let char = arr[0];
    for(let i = 0; i < arr.length - 1; i ++) {
        if(arr[i] !== arr[i+1]) {
            if(sum%2 === 1) {  //出现次数 sum 是奇数
                return char;
            }
            char = arr[i+1];
            sum =1;
        } else {    // 相邻两个元素相等
            sum ++;
        }
    }
    return arr[arr.length-1];  // 返回最后一个数组元素
}

14. 找到高于给定值的最小功率(Find the smallest power higher than a given a value)

描述

我们有号码12385。我们想知道最接近但高于 12385 的立方体的值。答案是13824。 现在,另一个案例。我们有号码1245678。我们想知道 5 次方,最接近且高于该数字。值将为1419857。

我们需要一个函数(在 JavaScript、CoffeeScript 和 Haskell 中),它接收两个参数、一个值和幂的指数,并输出我们想要查找的值。 findNextPower (val,pow_)

示例

findNextPower(12385, 3) == 13824

findNextPower(1245678, 5) == 1419857

思路一(有个小Bug)

不是要求比 val 更高的 pow_ 次方数吗,我们可以先求 val 的 1 / pow_ 次方数,设它为 a,再判断 a 是否为整数,如果是整数, 返回(a+1)的 pow_ 次方,如果不是,则让 a 向上取整,再返回它的 pow_ 次方。

代码一

const findNextPower = (val,pow_) => {
    var a = Math.pow(val, 1/pow_);
    if(a % 1 == 0) {
        return (a + 1) ** pow_;
    } else {
        return Math.ceil(a) ** pow_;
    }

}

解法一的bug

当输入的是 findNextPower(4782969, 7), 其中 4782969 是 9 的 7 次方,按理说,那么 4782969 的 1 / 7 次方应该等于 9,但是打印结果发现它等于 8.999999999999998,用在这里的话,最后返回结果为 4782969,这是不正确的。

这是为什么呢?

因为精度丢失问题,计算机能读懂的是二进制数,当进行计算时,实际上是把数字转换为了二进制进行的,这个过程中丢失了精度

优化思路一

对于上面的问题,可以进一步改进,要求的是比 val 高的次方数(一定是整数),那就至少要比 val 大 1, 我们可以先让得到的 val 加一, 再求它的 1 / pow_ 次方,这样得到的数,肯定是比 val 的 1 / pow_ 次方大,就不用判断 1 / pow_ 是否为整数的情况, 而且也避免了精度丢失问题。

优化代码一

const findNextPower = (val,pow_) =>  Math.pow(Math.ceil(Math.pow(val + 1, 1 / pow_)), pow_)

思路二

直接用暴力破解法,直接进入 for 循环,从 1 开始遍历每个整数,每次循环中判断它的 pow_ 次方是否大于 val

代码二

const findNextPower = (val,pow_) => {
    for(let i = 1; i <= val; i ++) {
        if(Math.pow(i, pow_) > val) {
            return Math.pow(i, pow_)
        }
    }
}

15. 创建电话号码(Create Phone Number)

描述

编写一个接受 10 个整数(介于 0 和 9 之间)的数组的函数,该数组以电话号码的形式返回这些数字的字符串。

返回的格式必须正确才能完成此质询。 不要忘记右括号后面的空格!

思路

只要返回的格式正确就可以,故有很多种方法,我认为其中最简单的方法就是用模板字符串。

代码

const createPhoneNumber = (numbers) => {
    numbers = numbers.join("");
  return `(${numbers.substr(0,3)}) ${numbers.substr(3,3)}-${numbers.substr(6)}`
}

16. 偶数斐波那契和(Even Fibonacci Sum)

描述

给出斐波那契数列中所有偶数的总和,直到(但不包括)传递给函数的数字。或者,换句话说,将所有小于给定数 n 的偶数斐波那契数(n 不是斐波那契数列的第 n 个元素)求和,而不包括 n。

fibonacci(max)

斐波那契数列是一系列数字,其中下一个值是前两个值的相加。该系列以 0 和 1 开头:

0 1 1 2 3 5 8 13 21...

示例

fibonacci(0)==0

fibonacci(33)==10

fibonacci(25997544)==19544084

思路

定义两个变量 x, y 初始值为斐波那契数列的第一项和第二项(也就是0和1)(要保证 x 始终是小的那个, y 始终是大的那个,之后会说),定义一个变量 sum 用来存放偶数和,初始值等于 x(也就是0),用 while 循环,当满足 y < max 时,进入 while 循环,循环内部,先判断 y 是否为偶数,若是,则让 sum += y,若不是则往下执行,将 x + y 的值给 y ,而原来的 y 的值给 x(这里的实现可以看代码),这样,y始终是较大的那个,x始终是较小的那个,故循环条件只需要比较 y 和 max 的大小。最后出循环返回 sum。

始终保持 y 是较大的数,这样每次 if, while 只需要对 y 进行判断。 不然 x, y 交叉着来,想想就头大

代码

const fibonacci = (max) => {
    let x = 0;
    let y = 1;
    let sum = x;   
    while(y < max) {   
        if(y % 2 ===0){    // 判断 y 是否为偶数
            sum += y;
        }
        let z = x + y;   // 把 x + y 的值给变量 z
        x = y;    // 将 y 原来的值赋值给 x
        y = z;    // 再将原来的两个相加和 z 赋值给 y
    }
    return sum;
}

17. 谁喜欢它?(Who likes it?)

描述

您可能从Facebook和其他页面上知道“喜欢”系统。人们可以“喜欢”博客文章、图片或其他项目。我们希望创建应该显示在此类项目旁边的文本。 实现一个函数,该函数采用一个数组,其中包含喜欢某个项目的人的姓名。它必须返回显示文本,如示例中所示:

示例

[] --> "no one likes this"

["Peter"] --> "Peter likes this"

["Jacob", "Alex"] --> "Jacob and Alex like this"

["Max", "John", "Mark"] --> "Max, John and Mark like this"

["Alex", "Jacob", "Mark", "Max"] --> "Alex, Jacob and 2 others like this"

思路

就是跟前面第 16 题差不多,直接字符串模板就行,很简单,但我为什么要再拿出来呢?虽然有点水题目的嫌疑,但是!!我想说的是! 这道题我提交的时候前前后后改了五六遍, 因为!又是 like后面少了 s ,后面又有些 likes 后面多了一个 s,注意看前面两个是 likes ,后面的是 like,我英语不太好,这是第三人称单数复数的意思,我就看了第一行的 like, 后面都用的 like。

补充

我想说的是! 之后看题的时候,敲代码的时候,一定要细心认真读题看题,认真敲代码,经常有时候因为一个类名写错,或者单词写错了个字母,导致效果无法实现,以至于重新去排查哪里出错,浪费了很多时间。

认真读题! 认真敲代码!

代码

function likes(names) {
    if(names.length == 0) {
      return "no one likes this"
    }
    if (names.length == 1) {
      return `${names[0]} likes this`
    }
    if (names.length == 2) {
      return `${names[0]} and ${names[1]} like this`
    }
    if (names.length == 3) {
      return `${names[0]}, ${names[1]} and ${names[2]} like this`
    } 
    if (names.length > 3){
      let a = names.length - 2;
      return `${names[0]}, ${names[1]} and ${a} others like this`
    }
  }

18. 数字是其数字总和的幂(Numbers that are a power of their sum of digits)

描述

数字81有一个特殊的性质,其数字之和的一定幂等于81(九的平方)。八十一 (81) 是具有此属性的第一个数字(不考虑一位数)。 下一个,是512。 让我们看看这两种情况的细节

8 + 1 = 9 和 9^2 = 81

512 = 5 + 1 + 2 = 8 和 8^3 = 512

我们需要创建一个函数,它接收一个数字作为参数并返回这个数字序列。powerSumDigTerm(n)

示例

1 --> 81

2 --> 512

思路

可以先声明一个空数组 arr,然后将符合要求的数都 push 进去,再用 sort() 方法给数组排序,最后根据输入的 n ,来返回 arr[n - 1]。(因为数组元素下标从0开始)

现在问题就来到了怎么找符合要求的数。这种数,只要足够大,就会有无穷多个,我们只需要考虑部分就行。用两层 for 循环,初始 i, j 都为2(1和0不考虑),外层循环98次,内层循环40次,每次 i, j自增。每次循环中,求 i 的 j 次方,再求得到的乘积的各项数字和,判断和是否等于i,若等于,则将乘积 push 进数组。最后能得到拥有一些元素的数组。

此方法只能考虑部分情况,若给的 n 过大,则无法输出

代码

let arr = [];
for(let i = 2; i < 100; i ++) {
    for(let j = 2; j < 42; j ++) {
        let product = Math.pow(i, j)
        if(product.toString().split("").reduce((total,current) => total + parseInt(current),0) === i) {
            arr.push(product)
        }
    }
}
arr = arr.sort((a, b) => a - b);
const powerSumDigTerm = (n) => arr[n - 1];

19. 数字总和或数字根(Sum of Digits / Digital Root)

描述

数字根是数字中所有数字的递归和。

给定,取数字的总和。如果该值有多个数字,请继续以这种方式减小,直到生成个位数数字。输入将为非负整数。

示例

16 --> 1 + 6 = 7

942 --> 9 + 4 + 2 = 15 --> 1 + 5 = 6

132189 --> 1 + 3 + 2 + 1 + 8 + 9 = 24 --> 2 + 4 = 6

493193 --> 4 + 9 + 3 + 1 + 9 + 3 = 29 --> 2 + 9 = 11 --> 1 + 1 = 2

思路

首先要得到这个数的各位数相加的和,声明一个变量 sum = 0 用来记录各项和,

可以用 toString() 和 split("") 将数字的每一项存入数组中,此时数组的每一项都是字符串类型,再用 map() 方法为每一项调用 Number() 将将其转为数字类型,再调用一次 map() 让每一项都加到 sum 中。

再判断比较 sum 和 9 的大小,若大于,继续递归调用函数本身,否则返回 sum

代码

const digitalRoot = (n) =>{
    // console.log("执行一次函数")
    let sum = 0;
    n.toString().split("").map((item) => Number(item)).map((item) => sum+=item)
    // console.log('此时sum=' + sum);
    if(sum > 9) {
        return (digitalRoot(sum));          // 这里要加 return ,不然没有返回值
    } 
    return sum;
  }

20. 建造一堆立方体(Build a pile of Cubes)

描述

你的任务是建造一座由n个立方体组成的建筑。 底部的立方体的体积为n^3,上面的立方体 将具有(n−1)^3依此类推,直到顶部的体积为1^3

您将获得建筑物的总体积m。 给定 m 你能找到你必须构建的立方体的数量 n 个吗?

函数 findNb 的参数将是一个整数 m 并且您必须返回整数 n,例如(find_nb, find-nb, findNb, ...)n^3+(n−1)^3+(n−2)^3+...+1^3=m, 如果存在这样的 n,如果没有这样的 n,则为 -1。

findNb(m)

示例

findNb(1071225) --> 45

findNb(91716553919377) --> -1

思路

我们可以从建筑的顶端开始从上往下,这样问题就变成了 1^3 + 2^3 + ... + n^3, 用 for 循环即可,比较总和 sum 和体积 m 的值,相等时,返回此时的 i,如果大于则返回 -1,否则继续循环。

代码

function findNb(m) {
    let sum = 0;
    for(let i = 1; ;i ++) {
        sum += i ** 3;
        if(sum === m) {
            return i;
        }
        if(sum > m) {
            return -1;
        }
    }
}