Freecodecamp 刷题记录——前端高级算法

Advanced Algorithm Scripting

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

创建一个函数,接受两个或多个数组,返回所给数组的 对等差分(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中把一个数组的内容全部赋值给另外一个数组 - …_博客园

Inventory Update

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'

No repeats please

把一个字符串中的字符重新排列生成新的字符串,返回新生成的字符串里没有连续重复字符的字符串个数.连续重复只以单个字符为准

例如, 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:“开始和结束日期如果在同一个月”指的是同一年&&同一月。

Make a Person

基于原型的语言(如 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);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值