FCC上的javascript算法题之中级篇
FCC中的javascript中级算法题解答
中级算法的题目中用到了很多js的知识点,比如迭代,闭包,以及对json数据的使用等等,现在将自己中级算法的解答思路整理出来供大家参考讨论。欢迎大家提出新的思路,写出更简单的解法。
1.给一个包含两个数字的数组。返回这两个数字和它们之间所有数字的和。
说明:最小的数字并非总在最前面
如:sumAll([4, 1])
应该返回 10。
sumAll([5, 10])
应该返回 45。
function sumAll(arr) { var max=Math.max(arr[0],arr[1]); var min=Math.min(arr[0],arr[1]); var num=0; for(var i=min; i<=max; i++){ num+=i; } return num; }
2.比较两个数组,然后返回一个新数组,该数组的元素为两个给定数组中所有独有的数组元素。换言之,返回两个数组的差异。
如:[1, 2, 3, 5], [1, 2, 3, 4, 5]
应该返回 [4]
。
[1, "calf", 3, "piglet"], [1, "calf", 3, 4]
应该返回 ["piglet", 4]
。
function diff(arr1, arr2) { var newArr = []; //过滤数组1中与数组2相等的项 var arr1Filtered=arr1.filter(function(num){ for(var i=0; i<arr2.length; i++){ if(num==arr2[i]){ return false; } } return true; }); //过滤数组2中与数组1相等的项 var arr2Filtered=arr2.filter(function(num){ for(var i=0; i<arr1.length; i++){ if(num==arr1[i]){ return false; } } return true; }); //连接两个数组 newArr=arr1Filtered.concat(arr2Filtered); return newArr; }
3.将给定的数字转换成罗马数字。
说明:关于罗马数字请参考 http://www.baike.com/wiki/%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97&prd=so_1_doc
如:convert(2)
应该返回 "II"。
convert(16)
应该返回 "XVI"。
convert(501)
应该返回 "DI"。
convert(3999)
应该返回 "MMMCMXCIX"。
function convert(num) { var one=''; var two=''; var three=''; var four=''; var arr=num.toString().split(''); //对不同位数传入不同参数,调用toRomChar函数 one=toRomChar(arr[arr.length-1],'I','V','X'); two=toRomChar(arr[arr.length-2],'X','L','C'); three=toRomChar(arr[arr.length-3],'C','D','M'); four=toRomChar(arr[arr.length-4],'M','',''); return four+three+two+one; } function toRomChar(oneNum,char1,char2,char3){ var rom=""; //数字小于4时,输出'III'形式 if(oneNum<4){ for(var i=0; i<oneNum; i++){ rom+=char1; } } //数字等于于4时,输出'IV'形式 else if(oneNum==4){ rom=char1+char2; } //数字大于4小于9时,输出'VIII'形式 else if(4<oneNum&&oneNum<=8){ rom=char2; for(var k=0; k<oneNum-5; k++){ rom+=char1; } } //数字等于9时,输出'I '形式 else if(oneNum==9){ rom=char1+char3; } return rom; }
4.写一个 function,它遍历一个对象数组(第一个参数)并返回一个包含相匹配的属性-值对(第二个参数)的所有对象的数组。如果返回的数组中包含 source 对象的属性-值对,那么此对象的每一个属性-值对都必须存在于 collection 的对象中。
说明:例如,如果第一个参数是 [{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }]
,第二个参数是 { last: "Capulet" }
,那么你必须从数组(第一个参数)返回其中的第三个对象,因为它包含了作为第二个参数传递的属性-值对。
如:where([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" })
应该返回 [{ first: "Tybalt", last: "Capulet" }]
。
where([{ "a": 1 }, { "a": 1 }, { "a": 1, "b": 2 }], { "a": 1 })
应该返回 [{ "a": 1 }, { "a": 1 }, { "a": 1, "b": 2 }]
。
where([{ "a": 1, "b": 2 }, { "a": 1 }, { "a": 1, "b": 2, "c": 2 }], { "a": 1, "b": 2 })
应该返回 [{ "a": 1, "b": 2 }, { "a": 1, "b": 2, "c": 2 }]
。
where([{ "a": 1, "b": 2 }, { "a": 1 }, { "a": 1, "b": 2, "c": 2 }], { "a": 1, "c": 2 })
应该返回 [{ "a": 1, "b": 2, "c": 2 }]
。
注:此题目描述不易理解,可结合参数及要返回的值进行理解
function where(collection, source) { var arr = []; var porp=Object.keys(source); arr=collection.filter(function(obj){ for(var i=0; i<porp.length; i++){ if(obj[porp[i]]!==source[porp[i]]){ //判断参数1中各个对象的porp属性的值是否与参数二中的porp属性值相等 return false; } } return true; }); return arr; }
5.使用给定的参数对句子执行一次查找和替换,然后返回新句子。
说明:
第一个参数是将要对其执行查找和替换的句子。
第二个参数是将被替换掉的单词(替换前的单词)。
第三个参数用于替换第二个参数(替换后的单词)。
注意:替换时保持原单词的大小写。例如,如果你想用单词 "dog" 替换单词 "Book" ,你应该替换成 "Dog"。
如:myReplace("Let us go to the store", "store", "mall")
应该返回 "Let us go to the mall"。
myReplace("His name is Tom", "Tom", "john")
应该返回 "His name is John"。
function myReplace(str, before, after) { //判断before字符串首字母是否大写,若是,则将after字符串首字母也替换为大写; if(before.charAt(0)<'a'){ var a=after.charAt(0); after=after.replace(a,a.toUpperCase()); } str=str.replace(before,after); return str; }
6.把指定的字符串翻译成 pig latin。
说明:PIg latin是把一个英文单词的第一个辅音或辅音丛(consonant cluster)移到词尾,然后加上后缀 "ay"。如果单词以元音开始,你只需要在词尾添加 "way" 就可以了。
如:translate("california")
应该返回 "aliforniacay"。
translate("eight")
应该返回 "eightway"。
translate("glove")
应该返回 "oveglay"。
function translate(str) { var p=/[^aeiou]/; var index=0; //计算出第一个元音字母在字符串中的位置 for(var i=0; i<str.length; i++){ if(p.test(str.charAt(i))){ continue; } index = i; break; } //元音字母为首字母时,给字符串加way后缀 if(index===0){ str+='way'; } //将辅音丛加至结尾,并加上'ay'后缀 else{ var arr=str.split(''); var del=arr.splice(0,index); arr=arr.concat(del); str=arr.join('') + 'ay'; } return str; }
7.Base pairs 是一对 AT 和 CG,为给定的字母匹配缺失的碱基。
说明:例如,对于输入的 GCG,相应地返回 [["G", "C"], ["C","G"],["G", "C"]]
如:pair("ATCGA")
应该返回 [["A","T"],["T","A"],["C","G"],["G","C"],["A","T"]]
。
pair("CTCTA")
应该返回 [["C","G"],["T","A"],["C","G"],["T","A"],["A","T"]]
。
function pair(str) { var newArr=[]; var arr=str.split(''); for(var i=0; i<arr.length; i++){ switch(arr[i]){ case 'A': newArr.push(['A','T']); break; case 'T': newArr.push(['T','A']); break; case 'G': newArr.push(['G','C']); break; case 'C': newArr.push(['C','G']); break; } } return newArr; }
8.从传递进来的字母序列中找到缺失的字母并返回它。
如:fearNotLetter("abce")
应该返回 "d"。
function fearNotLetter(str) { //将字符串转为ASCII码,并存入数组 var arr=[]; for(var i=0; i<str.length; i++){ arr.push(str.charCodeAt(i)); } for(var j=1; j<arr.length; j++){ var num=arr[j]-arr[j-1]; //判断后一项减前一项是否为1,若不为1,则缺失该字符的前一项 if(num!=1){ //将缺失字符ASCII转为字符并返回 return String.fromCharCode(arr[j]-1); } } return undefined; }
9.检查一个值是否是基本布尔类型,并返回 true 或 false。
function boo(bool) { // What is the new fad diet for ghost developers? The Boolean. if(bool===false){ return true; } else if(bool===true){ return true; } else{ return false; } }
10.写一个 function,传入两个或两个以上的数组,返回一个以给定的原始数组排序的不包含重复值的新数组。
说明:所有数组中的所有值都应该以原始顺序被包含在内,但是在最终的数组中不包含重复值。
如:unite([1, 3, 2], [5, 2, 1, 4], [2, 1])
应该返回 [1, 3, 2, 5, 4]
。
unite([1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8])
应该返回 [1, 2, 3, 5, 4, 6, 7, 8]
。
function unite(arr1, arr2, arr3) { for(var j=1; j<arguments.length; j++){ //过滤掉第j个数组中已经在前面出现过的值 var filteredArr=arguments[j].filter(function(num){ for(var i=0; i<arr1.length; i++){ if(arr1[i]==num){ return false; } } return true; }); arr1=arr1.concat(filteredArr); } return arr1; }
11.将字符串中的字符 &
、<
、>
、"
(双引号), 以及 '
(单引号)转换为它们对应的 HTML 实体。
如:convert("Dolce & Gabbana")
应该返回 Dolce & Gabbana
。
convert("Sixty > twelve")
应该返回 Sixty > twelve
。
function convert(str) { var p=/[&<>"']/g; str=str.replace(p,function(char){ switch(char){ case '&': return '&'; case '<': return '<'; case '>': return '>'; case '"': return '"'; case "'": return '''; } }); return str; }
12.将字符串转换为 spinal case。Spinal case 是 all-lowercase-words-joined-by-dashes 这种形式的,也就是以连字符连接所有小写单词。
如:spinalCase("The_Andy_Griffith_Show")
应该返回 "the-andy-griffith-show"
。
spinalCase("This Is Spinal Tap")
应该返回 "this-is-spinal-tap"
。
function spinalCase(str) { // "It's such a fine line between stupid, and clever." // --David St. Hubbins var newStr=''; var p=/[\s_]/g; if(!p.test(str)){ var arr=str.split(''); for(var i=0; i<arr.length; i++){ if(arr[i]<'a'){ newStr=newStr+' '; } newStr=newStr+arr[i]; } str=newStr; } //判断传入字符串中间是否含有空字符,若没有则在各单词间加入空格 str=str.toLowerCase(); str=str.replace(p,'-'); //将字符串转为小写并将其中的空格及下划线替换为中划线 return str; }
13.给一个正整数num
,返回小于或等于num
的斐波纳契奇数之和。
说明:斐波纳契数列中的前几个数字是 1、1、2、3、5 和 8,随后的每一个数字都是前两个数字之和。
如:sumFibs(4)应该返回 5。
sumFibs(1000)
应该返回 1785。
sumFibs(75025)
应该返回 135721。function sumFibs(num) { //求得小于num的斐波那契数列 注意:此数组最后一项大于num var arr=[1,1]; for(var i=0; i<num; i++){ arr[i+2]=arr[i]+arr[i+1]; if (arr[i+2]>num){ break; } } //删除最后一项,过滤掉数列中的偶数项 arr.splice(-1,1); arr=arr.filter(function(n){ if(n%2===0){ return false; } return true; }); //对数列进行求和 var result=arr.reduce(function(a,b){ return a+b; }); return result; }
14.求小于等于给定数值的质数之和。
说明:只有 1 和它本身两个约数的数叫质数。例如,2 是质数,因为它只能被 1 和 2 整除。1 不是质数,因为它只能被自身整除。给定的数不一定是质数。
如:sumPrimes(10)
应该返回 17。
function sumPrimes(num) { //将所有小于等于num的质数放进一个数组 var arr=[]; //1不是质数,从2开始循环,需算上num for(var i=2; i<=num; i++){ var j=2; //判断i能否被从2开始的数整除 while(i%j!==0){ j++; } //判断这个数是不是自身,是则加进数组 if(i==j){ arr.push(i); } } //对数组求和 var result=arr.reduce(function (a,b){return a+b;}); return result; }
15.找出能被两个给定参数和它们之间的连续数字整除的最小公倍数。
说明:范围是两个数字构成的数组,两个数字不一定按数字顺序排序。例如对 1 和 3 —— 找出能被 1 和 3 和它们之间所有数字整除的最小公倍数。
如:smallestCommons([1, 5])
应该返回 60。
smallestCommons([5, 1])
应该返回 60。
smallestCommons([1, 13])
应该返回 360360。
function smallestCommons(arr) { /* *此算法的思路是,先将传入的数组排序并补全,然后用数组的最后一项依次除以数组的其它各项,如果不能整 *除则给最后一项加上原来数组的最后一项的值,重新进行前面的判断,直到数组中所有项均可被整除,即可 *得到传入数组中所包含的数的最小公倍数 */ //对数组排序 arr.sort(function(a,b){return a-b;}); //补全数组 var arrLast=arr[arr.length-1]; for(var j=1; j<arrLast-arr[0]; j++){ arr.splice(j,0,arr[0]+j); } var k=0; var num=arr[arr.length-1]; while(k===0){ for(var i=0; i<arr.length-1 ;i++){ //判断数组中从第一个数起至倒数第二个数,能否被最后一个数整除,若不能则跳出内部循环 if(num%arr[i]!==0){ break; } //如果这个数可以被整除,而且这个数是数组的倒数第二个数时,结束外部循环 else if(i==arr.length-2){ k=1; } } //不能整除时,给num加上数组的最后一项的值 num+=arr[arr.length-1]; } return num-arr[arr.length-1]; }
16.写一个 function,它浏览数组(第一个参数)并返回数组中第一个通过某种方法(第二个参数)验证的元素。
如:find([1, 3, 5, 8, 9, 10], function(num) { return num % 2 === 0; })
应该返回 8。
find([1, 3, 5, 9], function(num) { return num % 2 === 0; })
应该返回 undefined。
function find(arr, func) { var newArr=arr.filter(func); return newArr[0]; }
17.弃数组(arr)的元素,从左边开始,直到回调函数return true就停止。
说明:第二个参数,func
,是一个函数。用来测试数组的第一个元素,如果返回fasle,就从数组中抛出该元素(注意:此时数组已被改变),继续测试数组的第一个元素,如果返回fasle,继续抛出,直到返回true。最后返回数组的剩余部分,如果没有剩余,就返回一个空数组。
如:drop([1, 2, 3, 4], function(n) {return n >= 3;})
应该返回 [3, 4]
。
drop([1, 2, 3, 9, 2], function(n) {return n > 2;})
应该返回 [3, 9, 2]
。
function drop(arr, func) { // Drop them elements. var i=0; do{ for(; i<arr.length; i++){ //遍历数组,若不满足回调函数,则将该项从数组中删除 if(!func.call(func,arr[i])){ //执行splice后数组长度减少1 arr.splice(i,1); break; } } } //判断减少一项后数组的第i项(即原数组中的第i+1项)是否满足回调函数,若不满足则继续循环,直到满足为止 while(!func.call(func,arr[i])); return arr; }
18.对嵌套的数组进行扁平化处理。你必须考虑到不同层级的嵌套。
如:steamroller([1, [2], [3, [[4]]]])
应该返回 [1, 2, 3, 4]
。
steamroller([1, [], [3, [[4]]]])
应该返回 [1, 3, 4]
。
steamroller([1, {}, [3, [[4]]]])
应该返回 [1, {}, 3, 4]
。
function steamroller(arr) { var newArr=[]; for(var i=0; i<arr.length; i++){ if(!Array.isArray(arr[i])){ newArr.push(arr[i]); }else{ newArr=newArr.concat(steamroller(arr[i])); } } return newArr; }
此题的解法是在一个for循环中使用了递归,在for循环中使用递归时,不会影响for循环的进程。
19.传入二进制字符串,翻译成英语句子并返回
说明:二进制字符串是以空格分隔的。
如:binaryAgent("01000001 01110010 01100101 01101110 00100111 01110100 00100000 01100010 01101111 01101110 01100110 01101001 01110010 01100101 01110011 00100000 01100110 01110101 01101110 00100001 00111111")
应该返回 "Aren't bonfires fun!?"
binaryAgent("01001001 00100000 01101100 01101111 01110110 01100101 00100000 01000110 01110010 01100101 01100101 01000011 01101111 01100100 01100101 01000011 01100001 01101101 01110000 00100001")
应该返回 "I love FreeCodeCamp!"
function binaryAgent(str) { var result=''; var arr=str.split(' '); for(var i=0; i<arr.length; i++){ //二进制转化为十进制 var num=parseInt(arr[i],2); str=String.fromCharCode(num); result+=str; } return result; }
20.完善编辑器中的every函数,如果集合(collection)中的所有对象都存在对应的属性(pre),并且属性(pre)对应的值为真。函数返回ture。反之,返回false。
如:every([{"user": "Tinky-Winky", "sex": "male"}, {"user": "Dipsy", "sex": "male"}, {"user": "Laa-Laa", "sex": "female"}, {"user": "Po", "sex": "female"}], "sex")
应该返回 true。
every([{"user": "Tinky-Winky", "sex": "male"}, {"user": "Dipsy"}, {"user": "Laa-Laa", "sex": "female"}, {"user": "Po", "sex": "female"}], "sex")
应该返回 false。
every([{"single": "double"}, {"single": undefined}], "single")
应该返回 false。
function every(collection, pre) { var result=collection.every(function(obj){ return obj[pre]; }); return result; }
21.创建一个计算两个参数之和的 function。如果只有一个参数,则返回一个 function,该 function 请求一个参数然后返回求和的结果
说明:例如,add(2, 3)
应该返回 5
,而 add(2)
应该返回一个 function。
如:add(2, 3)
应该返回 5。
add(2)(3)
应该返回 5。
add(2, "3")
应该返回 undefined。
function add() { if(arguments.length==1&&typeof arguments[0]=='number'){ var num = arguments[0]; return function(a){ if(typeof a=='number') return a+num; }; }else if(typeof arguments[0]=='number'&&typeof arguments[1]=='number'){ return arguments[0]+arguments[1]; }else{ return undefined; } }
具体题目可参考FCC中文网:https://freecodecamp.cn
总结一下,中级算法对js的很多概念的理解还是很有帮助的,推荐对js不够熟悉的同学练习一下。
文中的算法已尽可能的做到简洁高效,但依然有很大的改进空间,例如16题找出最小公倍数,我最初使用的方法是从1开始判断1是否是这个数组所有元素的最小公倍数,若不是则判断2是否为此数组的最小公倍数,以此递增,最终找出数组中所有元素的最小公倍数。此方法虽然可行,但性能极差,经过改进后的算法如文中16题中所给的代码,但这绝不是最优解,我相信大家都会有更好的思路。欢迎大家指出其中的不足,并给出自己的宝贵意见。
感谢阅读。