Freecodecamp 刷题记录——前端高级算法
Validate US Telephone Numbers
如果传入字符串是一个有效的美国电话号码,则返回 true.
用户可以在表单中填入一个任意有效美国电话号码. 下面是一些有效号码的例子(还有下面测试时用到的一些变体写法):
555-555-5555
(555)555-5555
(555) 555-5555
555 555 5555
5555555555
1 555 555 5555
在本节中你会看见如 800-692-7753 or 8oo-six427676;laskdjf这样的字符串. 你的任务就是验证前面给出的字符串是否是有效的美国电话号码. 区号是必须有的. 如果字符串中给出了国家代码, 你必须验证其是 1. 如果号码有效就返回 true ; 否则返回 false.
function telephoneCheck(str) {
// 祝你好运
var reg = new RegExp(/^(1\s?)?((\(\d{3}\))|\d{3})[\s-]?\d{3}[\s-]?\d{4}$/);
if (str.search(reg)>=0){
return true;
}
return false;
}
str = telephoneCheck("1(555)555-5555");
console.log(str);
创建一个函数,接受两个或多个数组,返回所给数组的 对等差分(symmetric difference) (△ or ⊕)数组.
给出两个集合 (如集合 A = {1, 2, 3} 和集合 B = {2, 3, 4}), 而数学术语 "对等差分" 的集合就是指由所有只在两个集合其中之一的元素组成的集合(A △ B = C = {1, 4}). 对于传入的额外集合 (如 D = {2, 3}), 你应该安装前面原则求前两个集合的结果与新集合的对等差分集合 (C △ D = {1, 4} △ {2, 3} = {1, 2, 3, 4}).
function sym() {
if (arguments.length === 2){
var arr1 = arguments[0];
var arr2 = arguments[1];
var tempArr = [];
return arr1.filter(function(item){
if (Array.from(arr2).indexOf(item) >= 0){
return false;
}
return true;
}).concat(arr2.filter(function(item){
if (Array.from(arr1).indexOf(item) >= 0){
return false;
}
return true;
})).filter(function(item){ //去重
if (tempArr.indexOf(item) >= 0){
return false;
}
else {
tempArr.push(item);
return true;
}
});
}
else if(arguments.length > 2){
var newArgs = [];
var oldArgs = arguments;
newArgs.push(sym(arguments[0], arguments[1]));
for (var i=2; i<oldArgs.length; i++){
newArgs.push(oldArgs[i]);
}
return sym.apply(this, newArgs); //将数组作为参数传递,需要用apply,而不能直接apply(newArgs),因为参数并不是数组
}
}
str = sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3]);
console.log(str);
tip1:sym.apply(this, newArgs); //将数组作为参数传递,需要用apply,而不能直接apply(newArgs),因为参数并不是数组;
tip2:注意需要对数组去重。
Exact Change
设计一个收银程序 checkCashRegister() ,其把购买价格(price)作为第一个参数 , 付款金额 (cash)作为第二个参数, 和收银机中零钱 (cid) 作为第三个参数.
cid 是一个二维数组,存着当前可用的找零.
当收银机中的钱不够找零时返回字符串 "Insufficient Funds". 如果正好则返回字符串 "Closed".
否则, 返回应找回的零钱列表,且由大到小存在二维数组中.
function checkCashRegister(price, cash, cid) {
var change = [];
//都换成整数,防止浮点数陷阱。
var VALUE = [1, 5, 10, 50, 100, 500, 1000, 2000, 10000];
var money = (cash - price) * 100;
cid = cid.map(function(item){
item[1] *= 100;
return item;
});
var i = 8;
while (money > 0){
if (i<0){
return "Insufficient Funds";
}
var sum = 0;
while (money>=VALUE[i] && cid[i][1]>0){
money -= VALUE[i];
cid[i][1] -= VALUE[i];
sum += VALUE[i];
}
if (sum>0){
//直接temp=cid的话,修改temp会影响cid的值,影响后面零钱是否用光的判断
var temp = cid.slice(); // this is how to make a copy
temp[1] = sum / 100;
change.push(temp);
}
i--;
}
//零钱是否用光
for (i=0; i<9; i++){
if (cid[i][1]>0){
return change;
}
}
return "Closed";
}
str = checkCashRegister(19.50, 20.00, [["PENNY", 0.50], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]]);
console.log(str);
tips1:数组是指针!赋值给其它数组后,是把指针传递了过去,新数组改变会影响原数组!!!
console.log('before:' + cid[i]);
var temp = cid;
temp[i][1] = sum / 100;
console.log('after:' + cid[i]);
打印结果:
before:PENNY,0
freecodecamp.js:28 after:PENNY,0.5
参考:javascript中把一个数组的内容全部赋值给另外一个数组 - ..._博客园
function updateInventory(arr1, arr2) {
for (var j=0; j<arr2.length; j++){
var i = 0;
//在有序表中寻找位置
while (i<arr1.length && arr2[j][1]>arr1[i][1]){
i++;
}
console.log(i);
if (i===arr1.length){
arr1.push(arr2[j]);
}
else{
//如果存在
if (arr1[i][1] === arr2[j][1]){
arr1[i][0] += arr2[j][0];
}
//如果未存在
else{
var tempArr = arr1.slice(0, i);
tempArr.push(arr2[j]);
arr1 = tempArr.concat(arr1.slice(i));
}
}
}
return arr1;
}
var curInv = [
[21, "Bowling Ball"],
[2, "Dirty Sock"],
[1, "Hair Pin"],
[5, "Microphone"]
];
var newInv = [
[2, "Hair Pin"],
[3, "Half-Eaten Apple"],
[67, "Bowling Ball"],
[7, "Toothpaste"]
];
str = updateInventory(curInv, newInv);
console.log(str);
tips1:
原先写的如下代码:
for (var j=0; j<arr2.length; j++){
var i = 0;
//在有序表中寻找位置
while (arr2[j][1][0] > arr1[i][1][0]){
i++;
}
……
}
报错:
freecodecamp.js:6 Uncaught TypeError: Cannot read property '1' of undefined
一开始以为是不能把arr2视为多维数组,对其直接取下标。而结果是,直接把参数视为多维数组没毛病,错误是没限制i的范围,我越界了!!!
tips2:
字符串可以直接比较大小,会根据第一个不同的字符的ascii值码进行比较,当数字(number)与字符串(string)进行比较大小时,会强制的将数字(number)转换成字符串(string)然后再进行比较。
例如:
console.log('13'>'3'); // 输出:false,因为第一位'1'<'3'
把一个字符串中的字符重新排列生成新的字符串,返回新生成的字符串里没有连续重复字符的字符串个数.连续重复只以单个字符为准
例如, aab 应该返回 2 因为它总共有6中排列 (aab, aab, aba, aba, baa, baa), 但是只有两个 (aba and aba)没有连续重复的字符 (在本例中是 a).
function swap(array, i, j){
if (i!=j){
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
function permAlone(str){
var count = 0;
function allSort(array, start, end) {
if (start < end - 1){
for (var i=start; i<end; i++){
swap(array, start, i);
allSort(array, start+1, end);
swap(array, start, i);
}
}
//得到一种全排列
else{
//判断是否有连续重复字符
//如果有,返回,不计数。
for (var j=0; j < array.length - 1; j++){
if (array[j]===array[j+1]){
break;
}
}
if (array[j]!==array[j+1]){
count++;
console.log(array);
}
}
return count;
}
//使用闭包,以便使allSort函数能直接修改count的值;
//将字符串转换为数组,否则string类型是无法修改的,函数只能传递形参,
//而数组作为对象则传递的是地址。
allSort(str.split(''), 0, str.length);
return count;
}
console.log(permAlone('aab'));
感觉是前端算法中最难的一道题了,用到递归的思路。
Friendly Date Ranges
让日期区间更友好!
把常见的日期格式如:YYYY-MM-DD 转换成一种更易读的格式。
易读格式应该是用月份名称代替月份数字,用序数词代替数字来表示天 (1st 代替 1).
记住不要显示那些可以被推测出来的信息: 如果一个日期区间里结束日期与开始日期相差小于一年,则结束日期就不用写年份了;在这种情况下,如果月份开始和结束日期如果在同一个月,则结束日期月份也不用写了。
另外, 如果开始日期年份是当前年份,且结束日期与开始日期小于一年,则开始日期的年份也不用写。
例如:
包含当前年份和相同月份的时候,makeFriendlyDates(["2017-01-02", "2017-01-05"]) 应该返回 ["January 2nd","5th"]
不包含当前年份,makeFriendlyDates(["2003-08-15", "2009-09-21"]) 应该返回 ["August 15th, 2003", "September 21st, 2009"]。
请考虑清楚所有可能出现的情况,包括传入的日期区间是否合理。对于不合理的日期区间,直接返回 undefined 即可
function dayAppendix(day){
var str;
switch (day){
case 1:
case 21:
case 31: str = "st";break;
case 2:
case 22: str = "nd";break;
case 3:
case 23: str = "rd";break;
default: str = "th";
}
return str;
}
function makeFriendlyDates(arr) {
var d1 = arr[0].split('-').map(function(item){
return parseInt(item);
});
var d2 = arr[1].split('-').map(function(item){
return parseInt(item);
});
if ((d1[0]>d2[0]) ||
(d1[0]===d2[0] && d1[1]>d2[1]) ||
(d1[0]===d2[0] && d1[1]===d2[1] && d1[2]>d2[2])){
return undefined;
}
var Month = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var retArr = [];
var str1 = Month[d1[1]-1] + " " + d1[2];
var str2 = "";
str1 += dayAppendix(d1[2]);
var withinAYear = false;
var deltaY = d2[0] - d1[0];
var deltaM = d2[1] - d1[1];
var deltaD = d2[2] - d1[2];
if ((deltaY===0)||
(deltaY===1 && deltaM<0)||
(deltaY===1 && deltaM===0 && deltaD<0)){
withinAYear = true;
}
var curYear = 2017;
if (d1[0]!==curYear || !withinAYear){
str1 = str1 + ', ' + d1[0];
}
retArr.push(str1);
if (deltaY===0 && deltaM===0 && deltaD===0){
return retArr;
}
if (d1[0]!==d2[0] || d1[1]!==d2[1] || !withinAYear){
str2 += Month[d2[1]-1] + ' ';
}
str2 += d2[2];
str2 += dayAppendix(d2[2]);
if (!withinAYear){
str2 = str2 + ', ' + d2[0];
}
retArr.push(str2);
return retArr;
}
str =makeFriendlyDates(["2010-10-23", "2011-10-22"]);
console.log(str);
tips1:英文日期简写是根据词尾判别的,即个位是1就是st,个位是2就是nd,个位是3就是rd,其他个位就是th,但11,12,13是例外,都用th。(根据读音,eleven总不能加st吧)
tips2:将多个条件写下来,验算逻辑,比较容易捋清楚。
tips3:“开始和结束日期如果在同一个月”指的是同一年&&同一月。
基于原型的语言(如 JavaScript)并不存在这种区别:它只有对象。基于原型的语言具有所谓原型对象的概念。原型对象可以作为一个模板,新对象可以从中获得原始的属性。任何对象都可以指定其自身的属性,既可以是创建时也可以在运行时创建。而且,任何对象都可以作为另一个对象的原型,从而允许后者共享前者的属性。
JavaScript构造函数及原型对象 - 王浴昊 - CSDN博客
var Person = function(firstAndLast) {
var arr = firstAndLast.split(' ');
var firstName = arr[0];
var lastName = arr[1];
this.getFirstName = function(){
return firstName;
};
this.getLastName = function(){
return lastName;
};
this.getFullName = function(){
return firstName + ' ' + lastName;
};
this.setFirstName = function(first){
firstName = first;
};
this.setLastName = function(last){
lastName = last;
};
this.setFullName = function(fullName){
firstAndLast = fullName;
var tempArr = fullName.split(' ');
firstName = tempArr[0];
lastName = tempArr[1];
};
};
var bob = new Person('Bob Ross');
console.log(bob);
Map the Debris
返回一个数组,其内容是把原数组中对应元素的平均海拔转换成其对应的轨道周期.
原数组中会包含格式化的对象内容,像这样 {name: 'name', avgAlt: avgAlt}
.
至于轨道周期怎么求,戳这里 on wikipedia (不想看英文的话可以自行搜索以轨道高度计算轨道周期的公式).
求得的值应该是一个与其最接近的整数,轨道是以地球为基准的.
地球半径是 6367.4447 kilometers, 地球的GM值是 398600.4418, 圆周率为Math.PI
function orbitalPeriod(arr) {
var GM = 398600.4418;
var earthRadius = 6367.4447;
return arr.map(function(obj){
var R = (obj.avgAlt + earthRadius);
var retObj = {};
retObj.name = obj.name;
retObj.orbitalPeriod = Math.round(2 * Math.PI * R * Math.pow(R/GM, 0.5));
return retObj;
});
}
str = orbitalPeriod([{name : "sputnik", avgAlt : 35873.5553}]);
console.log(str);
Pairwise
找到你的另一半
都说优秀的程序员擅长面向对象编程,但却经常找不到另一半,这是为什么呢?因为你总是把自己局限成为一个程序员,没有打开自己的思维。
这是一个社群的时代啊,在这里你应该找到与你有相同价值观但又互补的另一半。
譬如:你编程能力强,估值11分,如果以20分为最佳情侣来计算,你应该找一个设计能力强,估值为9分的女生。
那么当你遇到一个设计能力为9分的女生,千万别犹豫,大胆去表白。千万别以为后面的瓜比前面的甜哦。
举个例子:有一个能力数组[7,9,11,13,15],按照最佳组合值为20来计算,只有7+13和9+11两种组合。而7在数组的索引为0,13在数组的索引为3,9在数组的索引为1,11在数组的索引为2。
所以我们说函数:pairwise([7,9,11,13,15],20) 的返回值应该是0+3+1+2的和,即6。
function pairwise(arr, arg) {
var flag = [];
var sum = 0;
for (var i=0; i<arr.length; i++){
if (flag[i]){
continue;
}
for (var j=i+1; j<arr.length; j++){
if (flag[j]){
continue;
}
if ((arr[i]+arr[j])===arg){
flag[i] = true;
flag[j] = true;
sum += i;
sum += j;
break;
}
}
}
return sum;
}
pairwise([1,4,2,3,0,5], 7);