JS算法项目是5道题目,没有具体的知识点讲解,我把题目的解答和思路放在下面:
Introduction to the JavaScript Algorithms and Data Structures Projects
Palindrome Checker 测试回文
题干:测试字符串从左到右与从右到左是否是一致的(需要提前筛除掉-/等特殊符号)
这题比较简单,先把字符串转换成小写字母(题目规定忽略大小写差异),切分成数组,因为数组可以使用
reverse()
方法直接调转顺序,这里用split("")
切分,方便后一步筛选特殊符号,然后利用filter()
方法,用正则/[a-z0-9]/
把可以接受的字符筛选出来,再将正反两个顺序的数组用join()
方法连接起来比较就好了。1
2
3
4
5function palindrome(str) {
let arr = str.toLowerCase().split("").filter(ele => (/[a-z0-9]/).test(ele));
return arr.join("") === arr.reverse().join("");
}
palindrome("eye");
-
题干:将阿拉伯数字转换成罗马数字
我们先找出罗马数字的特征,罗马数字有特殊的字母符号来表示特定的数字(1, 5, 10, 50, 100, 500, 1000),这里只需要计算到最高3999。这些符号累加的结果等于阿拉伯数字,但是写的顺序要注意,如果较小的单位在较大单位的左边,则表示减去这个较小单位,如果在右边就是加上较小单位。
我采取的方法是先创建一个
Roman
对象来保存各个级别的对应字母,然后创建repeat
函数来规定字母怎么排列。这里以5作为切换的标准,1, 10, 100, 1000
这4个级别上都是1-3
个单位写作累加unit unit unit
类似III
或者XXX
,如果是4
个单位就是表示5倍单位的字母左边加上较小单位,如IV
或XL
,5
个单位的则是对应级别中数组的第二个元素,如V
,L
,D
,如果是6-8
个单位则是表示5倍单位的字母右边累计添加表示1倍单位的字母,最后表示9
个单位则是10倍单位在左边加上一倍单位。找到这个规律之后,找到每个级别的数字,
Math.floor(num / unit)
,再调用repeat
函数就可以返回转换后的字符串了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35function convertToRoman(num) {
let Roman = {
1: ["I", "V"],
10: ["X", "L"],
100: ["C", "D"],
1000: ["M"]
}
let result = "";
let repeat = function(times, prop) {
let roman = Roman[prop];
let addStr;
if(times <= 3) {
addStr = new Array(times).fill(roman[0]).join("");
result += addStr;
} else if (times === 4) {
addStr = roman[0] + roman[1];
result += addStr;
} else if (times === 5) {
result += roman[1];
} else if (times < 9) {
addStr = roman[1] + new Array(times-5).fill(roman[0]).join("");
result += addStr;
} else if (times === 9) {
addStr = roman[0] + Roman[prop * 10][0];
result += addStr;
}
return result;
}
repeat(Math.floor(num / 1000), 1000);
repeat(Math.floor((num % 1000) /100), 100);
repeat(Math.floor(num % 100 / 10), 10);
repeat(num % 10, 1);
return result;
}
convertToRoman(36);
-
题干:凯撒密码
凯撒密码是一个很简单的编码方式,即字母对应编码为字母本身Unicode编码+13的字母。这里有一个小坑是,如果统一加13,小于65或者大于90对应的字母在26个大写字母之外,所以需要判断一下字母的大小,如果字母的编码本身大于77,则编码-13,如果小于77,则编码+13。对于非字母,则不必转换。
1
2
3
4
5
6
7
8
9
10
11
12
13function rot13(str) { // LBH QVQ VG!
let result = str.split("").map(val => {
if(val.charCodeAt() < 65 || val.charCodeAt() > 90) {
return val;
} else {
return val.charCodeAt() > 77? String.fromCharCode(val.charCodeAt() - 13): String.fromCharCode(val.charCodeAt() + 13)
}
}).join("");
return result;
}
// Change the inputs below to test
rot13("SERR PBQR PNZC");
-
题干:检测电话号码是否有效
这题就是利用正则表达式来检测字符串。字符串开头可以是1或者1加上空格,之后是3个数字,这3个数字前后可以有一对括号,但是括号必须完整,之后是3个数组,再之后是4个数字,3组数字之间可以是空格,也可以是短横线,还可以没有分隔符,字符串前后不能再有其他的内容。所以按照这个思路很容易写出这个表达式。
1
2
3
4
5
6function telephoneCheck(str) {
let re = /^(1|1\s)?([0-9]{3}|\([0-9]{3}\))(\s|-)?[0-9]{3}(\s|-)?[0-9]{4}$/;
return re.test(str);
}
telephoneCheck("555-555-5555");
-
题干:模拟自动收银机返回找零钱的数组
这题和转换罗马数字那题有一点类似,就是使用对象会很方便。这题把所有的单位和单位指代的价格对应起来,放在一个对象中。
首先判断需要找零的钱和收银机里钱的关系,如果找零正好等于收银机的前,则返回原数组,且
status
是CLOSED
,如果找零大于收银机里的钱,则返回零钱不够INSUFFICIENT FUNDS
,如果零钱够,则需要判断返回的数组,这里我让cid
中的每一位去和零钱比较,如果单位比change
大,则不必返回这一部分,如果单位比change
小或者相等,则判断返回多少Math.floor(change / unit)
,在遍历完成后检查change
是否为零,为零表示收银机里的钱可以合适地找给顾客,如果不为零,则说明对应的单位无法满足正好加起来等于找零。需要注意一个坑是,JavaScript在做浮点运算的时候回出现多位小数的bug,非常坑。这里我采取的方法是
toFixed(2)
,但是另一个需要注意的是,toFixed
方法会返回一个字符串,所以无法直接合其他的数字比较,如果需要比较,得parseFloat
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41function checkCashRegister(price, cash, cid) {
var change = cash - price;
let money = {
"ONE HUNDRED": 100,
"TWENTY": 20,
"TEN": 10,
"FIVE": 5,
"ONE": 1,
"QUARTER": 0.25,
"DIME": 0.1,
"NICKEL": 0.05,
"PENNY": 0.01
};
let drawer = parseFloat(cid.reduce((acc, cur) => acc + cur[1], 0).toFixed(2));
if(change > drawer) {
return {status: "INSUFFICIENT_FUNDS", change:[]};
} else if (change === drawer) {
return {status: "CLOSED", change: cid};
} else {
let cashArr = [];
cid.reverse().map(val => {
let unit = money[val[0]];
let cache = Math.floor(change/unit);
if(cache > 0) {
if(cache * unit >= val[1]) {
cashArr.push(val);
change = (change - val[1]).toFixed(2);
} else {
cashArr.push([val[0], cache * unit]);
change = (change - cache * unit).toFixed(2);
}
}
})
if(eval(change) === 0) {
return {status: "OPEN", change: cashArr};
} else {
return {status: "INSUFFICIENT_FUNDS", change:[]};
}
}
}
checkCashRegister(19.5, 20, [["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.1], ["QUARTER", 4.25], ["ONE", 90], ["FIVE", 55], ["TEN", 20], ["TWENTY", 60], ["ONE HUNDRED", 100]]);