在freecodecamp上学习js进行到中级算法部分了,这里记录一下,方便后面查看。
1.Sum All Numbers in a Range
我们会传递给你一个包含两个数字的数组。返回这两个数字和它们之间所有数字的和。
最小的数字并非总在最前面。
注意:
- Math.min(val1[,val2,……,valn]),返回元素val1,val2,……valn中的最小值
- Math.max(val1[,val2,……,valn]),返回元素val1,val2,……valn中的最大值
arr.reduce(callback,[initialValue])
- callback执行数组中每个值的函数,包含四个参数(previousValue, currentValue, index, array)
- previousValue指上一次调用回调返回的值,或者是提供的初始值(initialValue)
- currentValue数组当前处理的元素
- index当前元素在数组中的索引
- array调用reduce的数组
- initialValue可选,作为第一次调用callback第一个参数
- [0,1,2,3,4].reduce(function(previousValue, currentValue, index, array){
return previousValue + currentValue;
});————–返回10=0+1+2+3+4 - [0,1,2,3,4].reduce(function(previousValue, currentValue, index, array){
return previousValue + currentValue;
}, 10);返回20=10+0+1+2+3+4 - 这个函数的使用暂时还不太熟,像这里提示中给出了这个函数,但我没用,后面要加强学习
function sumAll(arr) {
//存储最后返回的值
var num=0;
//挑选出arr数组中较小的值,作为循环的起点
var i=Math.min(arr[0],arr[1]);
//挑选出arr数组中较大的值,作为循环的终点
while(i<=Math.max(arr[0],arr[1])){
num+=i;
i++;
}
return num;
}
sumAll([1, 4]);
2.Diff Two Arrays
比较两个数组,然后返回一个新数组,该数组的元素为两个给定数组中所有独有的数组元素。换言之,返回两个数组的差异。
function diff(arr1, arr2) {
var newArr = [];
// Same, same; but different.
//在arr2中挑出arr1中没有的元素
var a1=arr1.filter(function(item,index,array){
return (arr2.indexOf(item)<0);
});
//在arr1中挑出arr2中没有的元素
var a2=arr2.filter(function(item,index,array){
return (arr1.indexOf(item)<0);
});
//合并数组中各自独有的部分a1和a2
newArr=a1.concat(a2);
return newArr;
}
diff([1, 2, 3, 5], [1, 2, 3, 4, 5]);
3.Roman Numeral Converter
将给定的数字转换成罗马数字。
所有返回的 罗马数字 都应该是大写形式。
分析:
- 这里首先要定义好数字和罗马数字之间的对应数组romanMatrix
- 采用递归的方法
- 这个题目比较有亮点,后期还需要复习
//定义部分数字和罗马数字之间的对应关系数组
var romanMatrix=[
[1000,'M'],
[900,'CM'],
[500,'D'],
[400,'DC'],
[100,'C'],
[90, 'XC'],
[50, 'L'],
[40, 'XL'],
[10, 'X'],
[9, 'IX'],
[5, 'V'],
[4, 'IV'],
[1, 'I']
];
function convert(num) {
//给定数字为0时,返回空值
if(num===0)
return '';
//递归找出给定数字对应的罗马数字
for(var i=0;i<romanMatrix.length;i++){
if(num>=romanMatrix[i][0])
return romanMatrix[i][1]+convert(num-romanMatrix[i][0]);
}
}
convert(88);
4.Where art thou
写一个 function,它遍历一个对象数组(第一个参数)并返回一个包含相匹配的属性-值对(第二个参数)的所有对象的数组。如果返回的数组中包含 source 对象的属性-值对,那么此对象的每一个属性-值对都必须存在于 collection 的对象中。
例如,如果第一个参数是 [{ first: “Romeo”, last: “Montague” }, { first: “Mercutio”, last: null }, { first: “Tybalt”, last: “Capulet” }],第二个参数是 { last: “Capulet” },那么你必须从数组(第一个参数)返回其中的第三个对象,因为它包含了作为第二个参数传递的属性-值对。
分析:
- 先循环保存source中key值和对应value值
- 再在collection中遍历比较collection[i]中对应key值和value值是否与source中的相等
- 若相等,就push到arr数组中,最终返回arr
function where(collection, source) {
var arr = [];
// What's in a name?
//循环保存source中key值
var key_val="";
//循环保存source中key值对应value,即source[key]
var value_val="";
//循环取出source中key和value
for(var key in source){
key_val=key;
value_val=source[key];
}
//循环比较collection中是否包含value=source[key]
for(var i=0;i<collection.length;i++){
for(var key in collection[i]){
if(key_val==key && value_val==collection[i][key])
//如果包含,push入arr数组尾
arr.push(collection[i]);
}
}
return arr;
}
where([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" });
5.Search and Replace
使用给定的参数对句子执行一次查找和替换,然后返回新句子。
第一个参数是将要对其执行查找和替换的句子。
第二个参数是将被替换掉的单词(替换前的单词)。
第三个参数用于替换第二个参数(替换后的单词)。
注意:替换时保持原单词的大小写。例如,如果你想用单词 “dog” 替换单词 “Book” ,你应该替换成 “Dog”。
//判断字符串str中首字符是否大写
function isFirstUpper(str){
if(str.charAt(0)==str.charAt(0).toUpperCase())
return true;
else
return false;
}
//将字符串str中首字符替换成大写形式
function firstToUpper(str){
return str.replace(str.charAt(0),str.charAt(0).toUpperCase());
}
function myReplace(str, before, after) {
//如果即将被替换的单词before中首字符为大写形式
//则将替换before的单词after首字符替换成大写形式
if(isFirstUpper(before)){
after=firstToUpper(after);
}
//将str中目标单词before用after替换
str=str.replace(before,after);
return str;
}
myReplace("A quick brown fox Jumped over the lazy dog", "Jumped", "leaped");
6.Pig Latin
把指定的字符串翻译成 pig latin。
Pig Latin 把一个英文单词的第一个辅音或辅音丛(consonant cluster)移到词尾,然后加上后缀 “ay”。
如果单词以元音开始,你只需要在词尾添加 “way” 就可以了。
function translate(str) {
//定义元音字母组成的字符数组
var vowel=['a','e','i','o','e'];
//保存str中开头的辅音丛的长度
var i=0;
var newstr=str;
//循环求出字符串str中开头的辅音丛的长度i
while(i<str.length){
//通过arr.indexOf判断字符是否是元音
//如果元音数组vowel中不包含该字符,则为辅音;
//否则str中首字符即为元音,直接退出循环
if(vowel.indexOf(str[i])==-1){
i++;
}
else{
break;
}
}
//如果str开头即为元音,则直接在字符尾部加上way
if(i==0){
newstr=newstr+"way";
}
//如果str开头存在辅音丛,则将str首的辅音丛直接移到字符串尾,并加上ay
else{
newstr=newstr.substr(i)+newstr.substr(0,i)+"ay";
}
return newstr;
}
translate("california");
7.DNA Pairing
DNA 链缺少配对的碱基。依据每一个碱基,为其找到配对的碱基,然后将结果作为第二个数组返回。
Base pairs(碱基对) 是一对 AT 和 CG,为给定的字母匹配缺失的碱基。
在每一个数组中将给定的字母作为第一个碱基返回。
例如,对于输入的 GCG,相应地返回 [[“G”, “C”], [“C”,”G”],[“G”, “C”]]
字母和与之配对的字母在一个数组内,然后所有数组再被组织起来封装进一个数组。
function pair(str) {
//以碱基对的配对关系定义map键值对
var map={
'A':'T',
'T':'A',
"G":'C',
'C':'G'
};
var arr=[];
var i=0;
var len=str.length;
while(i<len){
//循环依次从map中取出与键(str的各个字符)相对于的值value
var value=map[str[i].toUpperCase()];
//将key和value组成的数组push到arr数组中
arr.push([str[i],value]);
i++;
}
return arr;
}
pair("GCG");
8.Missing letters
从传递进来的字母序列中找到缺失的字母并返回它。
如果所有字母都在序列中,返回 undefined。
function fearNotLetter(str) {
var len=str.length;
//str中第一个字符的Unicode值
var start=str.charCodeAt(0);
//str中最后一个字符的Unicode值
var end=str.charCodeAt(len-1);
var arr=[];
var j=0;
for(var i=start;i<=end,j<len;i++,j++){
//循环遍历,如果str中字符不等于Unicode值i对应的字符
//说明str中该字符缺失,push入str数组中
//注意:str中数组下标j要保持不变,继续与下一个Unicode值所对应的字符比较
if(str[j]!=String.fromCharCode(i)){
arr.push(String.fromCharCode(i));
//为了保持下标j不变,这里要减1,因为循环会自动加1
j--;
}
}
if(arr.length===0)
return undefined;
else
//return arr.toString();
return arr.join('');
}
fearNotLetter("abcdefgho");
9.Boo who
检查一个值是否是基本布尔类型,并返回 true 或 false。
基本布尔类型即 true 和 false。
注意:
typeof operand
- typeof操作符返回操作数的类型(字符串)
- operand是一个表达式,表示对象或原始值,返回其类型
- 例如:typeof 3.14 === ‘number’;typeof “bla” === ‘string’;typeof true === ‘boolean’;
- typeof new String(“abc”) === ‘object’;typeof Math.sin === ‘function’;
- typeof undefined === ‘undefined’;typeof [1, 2, 4] === ‘object’;
function boo(bool) {
// What is the new fad diet for ghost developers? The Boolean.
//typeof bool 返回bool的类型,即boolean
return typeof bool==='boolean';
}
boo(1);
10.Sorted Union
写一个 function,传入两个或两个以上的数组,返回一个以给定的原始数组排序的不包含重复值的新数组。
换句话说,所有数组中的所有值都应该以原始顺序被包含在内,但是在最终的数组中不包含重复值。
非重复的数字应该以它们原始的顺序排序,但最终的数组不应该以数字顺序排序。
总结:
- arguments很神奇,可以直接检索到函数中传递的参数个数
- arr.indexOf(char),功能上类似于java中的contains函数
- 可以用于查找arr数组中是否包含某个char元素,有就返回对应索引;没有就返回-1
function unite() {
var arr=[];
//unite中参数总数目(即数组个数)
var len=arguments.length;
//控制遍历哪个数组(arguments[i],i可取0~len-1)
var i=0;
while(i<len){
//控制遍历的数组arguments[i]中元素下标
var j=0;
while(j<arguments[i].length){
//如果arr中没有索引为i的数组参数中的索引为j的元素
//将其push到arr中
if(arr.indexOf(arguments[i][j])==-1)
arr.push(arguments[i][j]);
j++;
}
i++;
}
return arr;
}
unite([1, 3, 2], [1, [5]], [2, [4]]);
11.Convert HTML Entities
将字符串中的字符 &、<、>、” (双引号), 以及 ’ (单引号)转换为它们对应的 HTML 实体。
注意:
- 正则表达式中,描述”、’等需要加上转义符号\
- &对应html实体为&;
- <对应html实体为<;
- >对应html实体为>;
- “对应html实体为";
- ‘对应html实体为&apos;
function convert(str) {
// :)
return str.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/\"/g,""").replace(/\'/g,"'");
}
convert("Dolce & Gabbana");
12.Spinal Tap Case
将字符串转换为 spinal case。Spinal case 是 all-lowercase-words-joined-by-dashes 这种形式的,也就是以连字符连接所有小写单词。
分析:
- 需要转换的字符串格式可以归为两类:
- 第一类是利用空格、下划线等符号分解命名(以下称符号类型)str.split(/\W|_/)
- 第二类是驼峰命名法(以下称驼峰类型)
- 首先把这两种字符串类型分开,可以将字符串的按照第一种符号类型分解,如果str.split(/\W|_/).length==1(即分解后的数组长度为1),就说明当前字符串是第二种驼峰类型
function spinalCase(str) {
// "It's such a fine line between stupid, and clever."
// --David St. Hubbins
//判断如果是驼峰命名法
//\W 匹配任意不是基本拉丁字母表中单词(字母数字下划线)字符的字符
//|_ 由于基本拉丁字母表中包含下划线,这里下划线要单独判断
if(str.split(/\W|_/).length==1){
for(var i=0;i<str.length;i++){
//对str中每个字符遍历判断,找到驼峰命名中的大写字母(/[A-Z]/.test()),即为单词的分界处
if(/[A-Z]/.test(str[i])){
//直接用-+大写字母对应小写字母替换大写字母处即可
str=str.replace(str[i],"-"+str[i].toLowerCase());
}
}
}
//采用其他字符和下划线为单词分界的情形
//直接转换成小写,再用正则分割,最后以-连接起来即可
else{
str=str.toLowerCase().split(/\W|_/).join("-");
}
return str;
}
spinalCase('thisIsSpinalTap');
13.Sum All Odd Fibonacci Numbers
返回所有小于传入数值的斐波那契数列中的奇数之和,如果传入的数值是斐波那契数,那么它也应该参与求和。
斐波那契数列中的前几个数字是 1、1、2、3、5 和 8,随后的每一个数字都是前两个数字之和。
例如,向 function 传入 4 应该返回 5,因为斐波那契数列中所有小于 4 的奇数是 1、1、3。
分析:
此题不需要真实现斐波拉契算法,只需要按照算法挑选出其中的奇数项,并求和即可
斐波拉契数的定义:F(n)=F(n-1)+F(n-2)+…+F(1),即提取小于等于传入值的斐波拉契数列中奇数项并相加即可
function sumFibs(num) {
//a、b、c用来进行斐波拉契算法,sum统计最终的和
var a=0,b=0,c=1,sum=0;
//只要c<=num,就进行斐波拉契算法循环统计sum
while(c<=num){
//如果c是斐波拉契中的奇数项
if(c%2==1)
sum+=c;
//控制斐波拉契循环进行,该算法中每一个数字都是前两个数字之和
a=b;
b=c;
c=a+b;
}
return sum;
}
sumFibs(4);
14.Sum All Primes
求小于等于给定数值的质数之和。
只有 1 和它本身两个约数的数叫质数。例如,2 是质数,因为它只能被 1 和 2 整除。1 不是质数,因为它只能被自身整除。
给定的数不一定是质数。
注意:
for(var i=2;i<=Math.sqrt(num);i++)这里进行了部分优化,可以减少循环次数:
判断一个数是否是质数,只需要判断是否能被2~√num整除即可。
//判断num是否是质数
function isPrime(num){
//标记是否是质数,初始值是true
var flag=true;
//质数:只能被1和本身整除
//如果num可以整除2~Math.sqrt(num)中的任意一个数,则num不是质数,改变标记flag,并终止循环
for(var i=2;i<=Math.sqrt(num);i++){
if(num%i===0)
{
flag=false;
break;
}
}
return flag;
}
function sumPrimes(num) {
var sum=0;
//循环判断i=2~num是否是质数,如果是,与sum求和
for(var i=2;i<=num;i++){
if(isPrime(i))
sum+=i;
}
return sum;
}
sumPrimes(10);
15.Smallest Common Multiple
找出能被两个给定参数和它们之间的连续数字整除的最小公倍数。
范围是两个数字构成的数组,两个数字不一定按数字顺序排序。
例如对 1 和 3 —— 找出能被 1 和 3 和它们之间所有数字整除的最小公倍数。
注意:
这里涉及到经典算法:求最大公约数gcd(greatest common divisor)和最小公倍数scm(smallest common multiple)
gcd(最大公约数)算法过程(欧几里德算法/辗转相除法)
有两整数a和b:
① a%b得余数c,即c=a%b
② 若c=0,则b即为两数的最大公约数
③ 若c≠0,则a=b,b=c,再回去执行①
scm算法(最小公倍数算法)
最小公倍数=两整数的乘积÷最大公约数,即scm=sqrt(a*b)/gcd(a,b)
//求val1和val2的最大公约数(greatest common divisor)
//欧几里德算法(辗转相除法)
function gcd(val1,val2){
if(val1%val2===0)
return val2;
else
return gcd(val2,val1%val2);
}
function smallestCommons(arr) {
//将arr按升序排序
arr=arr.sort(function(a,b){
return a-b;
});
//求a和b的最小公倍数scm(smallest common multiple)
//scm=abs(a*b)/gcd(a,b)
var val=arr[0];
//这里求多个数的最小公倍数:先求出两个数的scm1,再求scm1与第三个数的scm2……依次循环
for(var i=arr[0]+1;i<=arr[1];i++){
val *=i/gcd(val,i);
}
return val;
}
smallestCommons([1,5]);
16.Finders Keepers
写一个 function,它浏览数组(第一个参数)并返回数组中第一个通过某种方法(第二个参数)验证的元素。
注意:arr.filter(func),返回arr中满足函数func的值
function find(arr, func) {
//数组result中保存arr数组中满足func函数的值
var result=arr.filter(func);
//如果result数组中有值存在(即arr中有满足func函数的值),则返回数组中第一个元素
if(result.length)
return result[0];
else
return undefined;
}
find([1, 3, 5, 9], function(num) { return num % 2 === 0; });
17.Drop it
Drop the elements of an array (first argument), starting from the front, until the predicate (second argument) returns true.
给定两个参数,第一个参数是一个数组,第二个参数是一个测试函数,算法需要以数组形式弹出满足测试函数的数组元素。
注意:循环过程中,因为shift操作会直接改变数组arr,所以数组arr的长度len在不断变化,每次都要重新计算
function drop(arr, func) {
// Drop them elements.
//临时保存数组arr中首元素
var tmp;
//注意这里每一次循环都需要重新取得数组arr的长度len,因为数组通过shift等操作不断在变化
for(var i=0,len=arr.length;i<len;i++){
//提取出数组arr中的第一个元素,此时数组会改变(索引、长度均会改变)
tmp = arr.shift();
//如果数组中首元素满足func函数,停止抛弃不合条件的元素的动作
if(func(tmp)){
//将满足条件的元素再插入数组首位,此时数组也会发生变化
arr.unshift(tmp);
break;
}
}
return arr;
}
drop([1, 2, 3, 4], function(n) {return n > 5;});
18.Steamroller
Flatten a nested array. You must account for varying levels of nesting.
对嵌套的数组进行扁平化处理。你必须考虑到不同层级的嵌套。
注意:
arr.concat(value1[,value2,valueN])
- value2~valueN可选,代表需要与数组arr合并的数组非数组值
- 返回一个新数组,不修改原数组arr
Array.isArray(arr)
- 判断arr是否是一个数组
- 如果arr是数组则返回true,否则false
function steamroller(arr) {
//result数组保存最后的结果数组
var result = [];
for(var i = 0; i < arr.length; i ++) {
//如果arr[i]是数组,递归求出arr[i]的扁平化数组steamroller(arr[i]),与result数组拼接
if(Array.isArray(arr[i])) {
result = result.concat(steamroller(arr[i]));
}
//如果arr[i]不是数组,直接push到result数组
else {
result.push(arr[i]);
}
}
return result;
}
steamroller([1, [2], [3, [[4]]]]);
19.Binary Agents
Return an English translated sentence of the passed binary string.
传入二进制字符串,翻译成英语句子并返回。
二进制字符串是以空格分隔的。
注意:
parseInt(string, radix) 将给定的字符串以指定基数radix解析成为整数
这里使用parseInt(code[i], 2),将code[i]以基数2(即将其当做二进制串解析)解析为整数
function binaryAgent(str) {
//将二进制串str用空格分割成数组code
var code=str.split(' ');
//保存最终字符串数组
var result='';
for(var i=0;i<code.length;i++){
//将数组code[i]中二进制数通过parseInt(string, radix)转换成整数
//再通过函数String.fromCharCode(整数)得出对应字符
result+=String.fromCharCode(parseInt(code[i], 2));
}
return result;
}
binaryAgent("01000001 01110010 01100101 01101110 00100111 01110100 00100000 01100010 01101111 01101110 01100110 01101001 01110010 01100101 01110011 00100000 01100110 01110101 01101110 00100001 00111111");
20.
Check if the predicate (second argument) is truthy on all elements of a collection (first argument).
所有的东西都是真的!
完善编辑器中的every函数,如果集合(collection)中的所有对象都存在对应的属性(pre),并且属性(pre)对应的值为真。函数返回ture。反之,返回false。
function every(collection,pre){
for(var i=0;i<collection.length;i++){
//如果collection[i][pre]为假或collection[i]不存在pre属性
if(!collection[i][pre]){
return false;
}
}
return true;
}
every([{"user": "Tinky-Winky", "sex": "male"}, {"user": "Dipsy", "sex": "male"}, {"user": "Laa-Laa", "sex": "female"}, {"user": "Po", "sex": "female"}], "sex");
21.Arguments Optional
创建一个计算两个参数之和的 function。如果只有一个参数,则返回一个 function,该 function 请求一个参数然后返回求和的结果。
例如,add(2, 3) 应该返回 5,而 add(2) 应该返回一个 function。
调用这个有一个参数的返回的 function,返回求和的结果:
var sumTwoAnd = add(2);
sumTwoAnd(3) 返回 5。
如果两个参数都不是有效的数字,则返回 undefined。
注意:typeof 返回对象的类型(js总共有5种类型:null、number、string、boolean、undefined)
function add() {
//typeof 返回对象的类型(总共有5种类型:null、number、string、boolean、undefined)
//arguments[i]返回函数add中传入的dii个参数
if(typeof arguments[0]=="number" && typeof arguments[1]=="number"){
return arguments[0]+arguments[1];
}
else if(arguments.length==1 && typeof arguments[0]=="number"){
var x=arguments[0];
//如果只有一个数字型参数x,返回x和后面捕捉到的任意一个数字型参数y之和
return function(y){
if(typeof y =="number"){
return x+y;
}
};
}
}
add(2,3);