Freecodecamp 算法练习

初级算法

Reverse a String

这个简单,字符串 immutable,先 split 成数组再 reverse,最后 join

function reverseString(str) {
  return str.split('').reverse().join('');
}

Factorialize a Number

这个也是,使用 recurse

function factorialize(num) {
  if (num === 0) { return 1; }
  return num * factorialize(num-1);
}

Check for Palindromes

检测回文,即正反读都是一样的词,基础用法可以如1中检测是否正反相等,高级用法可使用递归

function palindrome(str) {
  str = str.toLowerCase().replace(/[^a-z|0-9]/g, "");
  if (str.length === 0){
    return true;
  }
  if (str[0] !== str[str.length-1]){
    return false;
  }
  else{
    return palindrome(str.slice(1,str.length - 1));
  }
}

Find the Longest Word in a String

找出字符串中最长的词的长度,可使用遍历,也可以 reduce,高级用法又用递归

function findLongestWord(str) {
  str = str.split(" ");
  if(str.length == 1){
    return str[0].length;
  }
  if(str[0].length >= str[1].length){
    str.splice(1,1);
    return findLongestWord(str.join(" "));
  }
  if(str[0].length <= str[1].length){
    return findLongestWord(str.slice(1,str.length).join(" "));
  }
}

Title Case a Sentence

让句中每个单词首字母大写,可用 replace,charAt 调用,高级用法使用 ES6 中的箭头函数(匿名函数),replcae 函数第二参数接收的参数即是此匿名函数。如下

function titleCase(str) {
  return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => (L).toUpperCase());
}

Return Largest Numbers in Arrays

输出二元数组下各数组中最大的数,可遍历两个数组比较,初始化一个变量存储;高级用法如下

function largestOfFour(arr) {
  return arr.map(Function.apply.bind(Math.max, null));
}

Confirm the Ending

进阶的 endwith……,害怕。可用 substring 倒取最后一个

function confirmEnding(str, target) {
  return str.substr(-target.length) === target;
}

Repeat a string repeat a string

重复一个字符串多遍,可用 while(num > 0),设置计数;以下方法调用了 repeat,使用的 test ? true : false 为三元条件运算。

function repeatStringNumTimes(str, num) {
  return num > 0 ? str.repeat(num) : '';
}

Truncate a string

缩短文本用…代替;基本使用 slice 实现

function truncateString(str, num) {
  if (str.length <= num) {
    return str;
  } else {
    return str.slice(0, num > 3 ? num - 3 : num) + '...';
  }
}

Chunky Monkey

按照第二个参数划分第一个数组参数,以下为高级用法

function chunkArrayInGroups(arr, size) {
  var newArr = [];
  var i = 0;
  while (i < arr.length) {
    newArr.push(arr.slice(i, i+size));
    i += size;
  }
  return newArr;
}

Slasher Flick

类似上一个,数组中只保留第二个参数之后的内容;直接用 slice 即可

function slasher(arr, howMany) {
  return arr.slice(howMany);
}

Mutations

检测第一个参数是否完全包含第二个参数,只需包含其元素;以下用法使用了 split,every,indexOf

function mutation(arr) {
  return arr[1].toLowerCase()
    .split('')
    .every(function(letter) {
      return arr[0].toLowerCase()
        .indexOf(letter) != -1;
    });
}

Falsy Bouncer

除掉假值,使用 filter

function bouncer(arr) {
  return arr.filter(Boolean);
}

Seek and Destroy

类似上题,只返回不包含后几个参数的原数组

function destroyer(arr) {
  var args = Array.prototype.slice.call(arguments);
  args.splice(0, 1);
  return arr.filter(function(element) {
    return args.indexOf(element) === -1;
  });
}

Where do I belong

将第二个参数插入数组,排序然后返回索引

function getIndexToIns(arr, num) {
  var index = arr.sort((curr, next) => curr > next)
    .findIndex((currNum)=> num <= currNum);
  return index === -1 ? arr.length : index;
}

Caesars Cipher

凯撒密码,把字符前后移动生成密文,以下用法使用了 fromCharCode,charCodeAt

function rot13(str) {
  return str.replace(/[A-Z]/g, (L) => String.fromCharCode(65 + (L.charCodeAt(0) - 65 + 13) % 26));
}

中级算法

Sum All Numbers in a Range

返回数组参数内的所有整数和,下列用法用到了展开运算符

function sumAll(arr) {
    var sum = 0;
    for (var i = Math.min(...arr); i <= Math.max(...arr); i++){
        sum += i;
    }
  return sum;
}

Diff Two Arrays

返回两行中不公有的元素,以下函数调用了 filter,concat,includes

function diffArray(arr1, arr2) {
    return arr
      .filter(el => !arr2.includes(el))
      .concat(
        arr2.filter(el => !arr1.includes(el))
      )
}

Roman Numeral Converter

对应阿拉伯数字对应罗马数字

var convertToRoman = function(num) {
  var decimalValue = [ 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ];
  var romanNumeral = [ 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I' ];
  var romanized = '';
  for (var index = 0; index < decimalValue.length; index++) {
    while (decimalValue[index] <= num) {
      romanized += romanNumeral[index];
      num -= decimalValue[index];
    }
  }
  return romanized;
}

Wherefore art thou

返回第二个参数作为值在第一个参数的数组,使用了 Object.key() 返回键

function whatIsInAName(collection, source) {
  var srcKeys = Object.keys(source);
  return collection.filter(function (obj) {
    return srcKeys.reduce(function (res, key) {
      return obj.hasOwnProperty(key) && obj[key] === source[key];
    }, false);
  });
}

Search and Replace

查找替换……

function myReplace(str, before, after) {
    function applyCasing(source, target) {
        var targetArr = target.split("");
        var sourceArr = source.split("");
        for (var i = 0; i < Math.min(targetArr.length, sourceArr.length); i++){
            if (/[A-Z]/.test(sourceArr[i])) {
                targetArr[i] = targetArr[i].toUpperCase();
            }
            else targetArr[i] = targetArr[i].toLowerCase();
        }
        return (targetArr.join(""));
    }
    return str.replace(before, applyCasing(before, after));
}

Pig Latin

元音开始的单词词尾加 way,非元音开头的将元音以前的移到最后加ay,解法如下

function translatePigLatin(str) {
    var strArr = [];
    var tmpChar;
    function isConsonant(char) {
        return !/[aeiou]/.test(char);
    }
    if (!isConsonant(str.charAt(0)))
        return str + "way";
    else
        strArr = str.split("");
    while (isConsonant(strArr[0])) {
        tmpChar = strArr.shift();
        strArr.push(tmpChar);
    }
 return strArr.join("")+"ay";
}

DNA Pairing

DNA 配对,定义数组对应键查找值

function pairElement(str) {
  var map = {T:'A', A:'T', G:'C', C:'G'};
  strArr = str.split('');
  for (var i=0;i<strArr.length;i++){
    strArr[i]=[strArr[i], map[strArr[i]]];
  }
 return strArr;
}

Missing letters

指定一段连续的字母,输出中间漏掉的;调用 fromCharCode 和 charCodeAt,类似初级16题

function fearNotLetter(str) {
  var allChars = '';
  var notChars = new RegExp('[^'+str+']','g');
  for (var i = 0; allChars[allChars.length-1] !== str[str.length-1] ; i++)
    allChars += String.fromCharCode(str[0].charCodeAt(0) + i);
  return allChars.match(notChars) ? allChars.match(notChars).join('') : undefined;
}

Boo who

检验是否为布尔值,输出 true 或 false

function booWho(bool) {
  return typeof bool === 'boolean';
}

Sorted Union

合并并去重二重数组;以下为神之解法……

function uniteUnique() {
  var concatArr = [];
  var i = 0;
  while (arguments[i]){
    concatArr = concatArr.concat(arguments[i]); i++;
  }
  uniqueArray = concatArr.filter(function(item, pos) {
    return concatArr.indexOf(item) == pos;
  });
  return uniqueArray;
}

Convert HTML Entities

HTML 特殊符号对应及转义;使用 object 存储,然后对应查找值,也使用 map 调用了函数返回数组

function convertHTML(str) {
  htmlEntities={
    '&':'&amp;',
    '<':'&lt;',
    '>':'&gt;',
    '\"':'&quot;',
    '\'':"&apos;"
  };
  return str.split('').map(function(entity){
    return htmlEntities[entity] || entity;
  }).join('');
}

Spinal Tap Case

断词改用连字符,用到了正则捕获组

function spinalCase(str) {
  str = str.replace(/([a-z])([A-Z])/g, '$1 $2');
  return str.toLowerCase().split(/(?:_| )+/).join('-');
}

Sum All Odd Fibonacci Numbers

返回小于参数的所有奇数的 fibonacci 数的和,使用了 reduce 累加

function sumFibs(num) {
  var arrFib = [1];
  for (var i = 1; i <=num;) {
      arrFib.push(i);
      i = arrFib[arrFib.length - 1] + arrFib[arrFib.length - 2];
  }
  var res = arrFib.reduce(function(prev, curr) {
      if (curr%2 !== 0) return prev + curr;
      else return prev;
    });
  return res;
}

Sum All Primes

对小于参数的质数求和,嵌套函数并使用递归

function sumPrimes(num) {
  function isPrime(number){
      for (i = 2; i <= number; i++){
          if(number % i === 0 && number!= i){
             return false;
          }
       }
      return true;
  }
  if (num === 1){
    return 0;
  }
  if(isPrime(num) === false){
    return sumPrimes(num - 1);
  }
  if(isPrime(num) === true){
    return num + sumPrimes(num - 1);
  }
}

Smallest Common Multiple

取 array 间所有数的最小公倍数

function smallestCommons(arr) {
    var range = [];
    for (var i = Math.max(arr[0], arr[1]); i >= Math.min(arr[0], arr[1]); i--) {
    range.push(i);
    }
    return range.reduce(function(previousValue, currentValue) {
    var gcdPrevCurr = gcd(previousValue, currentValue);
    return (previousValue * currentValue) / gcdPrevCurr;
    });
    function gcd(x, y) {
    if (y === 0)
        return x;
    else
        return gcd(y, x%y);
    }
}

Finders Keepers

返回满足函数的数组中第一个为真的值,直接使用 filter

function findElement(arr, func) {
  filterArr = arr.filter(func);
  return filterArr[0];
}

Drop it

返回满足函数的数组中元素;调用了 shift,这解法简直……

function dropElements(arr, func) {
  while(arr.length > 0 && !func(arr[0])) {
    arr.shift();
  }
  return arr;
}

Steamroller

抽取多层数组中的元素返回;使用递归,厉害厉害

function steamrollArray(arr) {
  return arr.reduce(function (flat, toFlatten) {
    return flat.concat(Array.isArray(toFlatten) ? steamrollArray(toFlatten) : toFlatten);
  }, []);
}

Binary Agents

解析二进制;调用了fromCharCode,parseInt,用以解析字符串

function binaryAgent(str) {
  return String.fromCharCode(...str.split(" ").map(function(char){ return parseInt(char, 2); }));
}

Everything Be True

检验第二个参数是否存在于所传入的数组,解法厉害

function truthCheck(collection, pre) {
  return collection.reduce(function(acc, next) {
    if (next[pre]) {
      return acc;
    }
    else {
      acc = false;
      return acc;
    }
  },true);
}

Arguments Optional

传入参数求和,若参数只有一个则返回函数接收第二个数,解法厉害

function addTogether() {
  var args = Array.from(arguments);
  return args.some(n => typeof n !== 'number') ? undefined: args.length > 1 ?
	args.reduce((acc, n) => acc += n, 0): (n) => typeof n === "number" ? n + args[0]: undefined;
}

高级算法

Validate US Telephone Numbers

验证美国电话号码,神长的正则……

function telephoneCheck(str) {
   var regex = /^(1\s?)?(\(\d{3}\)|\d{3})[\s\-]?\d{3}[\s\-]?\d{4}$/;
   return regex.test(str);
}

Symmetric Difference

对称差或者异或,即筛掉数组公有的元素,解法头疼

function sym() {
  const diff = (A, B) => new Set([...A].filter(n => !B.has(n)));
  const result = [...arguments]
    .map(arr => new Set(arr))
    .reduce((acc, set) => new Set([...diff(acc, set), ...diff(set, acc)]));
  return [...result];
}

Exact Change

找零钱的函数……神长

var denom = [
  { name: 'ONE HUNDRED', val: 100.00},
  { name: 'TWENTY', val: 20.00},
  { name: 'TEN', val: 10.00},
  { name: 'FIVE', val: 5.00},
  { name: 'ONE', val: 1.00},
  { name: 'QUARTER', val: 0.25},
  { name: 'DIME', val: 0.10},
  { name: 'NICKEL', val: 0.05},
  { name: 'PENNY', val: 0.01}
];
function checkCashRegister(price, cash, cid) {
  var change = cash - price;
  var register = cid.reduce(function(acc, curr) {
    acc.total += curr[1];
    acc[curr[0]] = curr[1];
    return acc;
  }, {total: 0});
  if (register.total === change) {
    return 'Closed';
  }
  if (register.total < change) {
    return 'Insufficient Funds';
  }
  var change_arr = denom.reduce(function(acc, curr) {
    var value = 0;
    while (register[curr.name] > 0 && change >= curr.val) {
      change -= curr.val;
      register[curr.name] -= curr.val;
      value += curr.val;
      change = Math.round(change * 100) / 100;
    }
    if (value > 0) {
        acc.push([ curr.name, value ]);
    }
    return acc; // Return the current Change Array
  }, []);
  if (change_arr.length < 1 || change > 0) {
    return "Insufficient Funds";
  }
  return change_arr;
}

Inventory Update

更新存货,使用了 forEach,对数组调用参数

function updateInventory(arr1, arr2) {
  var flag = 0;
  arr2.forEach(function(item) {
    flag = 0;
    arr1.forEach(function(list) {
      if(item[1] === list[1]) {
        list[0] += item[0];
        flag = 1;
      }
    });
    if(flag === 0)
      arr1.push(item);
  });
  return arr1.sort(function(a, b) {
    return a[1] > b[1] ? 1 : -1;
  });
}

No repeats please

返回排列组合不连续重复的数目,不是很懂

function permAlone(str) {
  var regex = /(.)\1+/g;
  var arr = str.split('');
  var permutations = [];
  var tmp;
  if (str.match(regex) !== null && str.match(regex)[0] === str) return 0;
  function swap(index1, index2) {
    tmp = arr[index1];
    arr[index1] = arr[index2];
    arr[index2] = tmp;
  }
  function generate(int) {
    if (int === 1) {
      permutations.push(arr.join(''));
    } else {
      for (var i = 0; i != int; ++i) {
        generate(int - 1);
        swap(int % 2 ? 0 : i, int - 1);
      }
    }
  }
  generate(arr.length);
  var filtered = permutations.filter(function(string) {
    return !string.match(regex);
  });
  return filtered.length;
}

Friendly Date Ranges

输入两个时间点,返回友好……的时间段

function makeFriendlyDates(str) {
  var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
  function convertDate(str) {
    var dateStr = str.split('-');
    return (new Date(Date.UTC(dateStr[0], dateStr[1] - 1, dateStr[2])));
  }
  function dateEnding(val) {
    switch (val) {
      case 1:
      case 21:
      case 31:
        return val + 'st';
      case 2:
      case 22:
        return val + 'nd';
      case 3:
      case 23:
        return val + 'rd';
      default:
        return val + 'th';
    }
  }
  function monthDiff(date1, date2) {
    var month2 = date2.getUTCFullYear() * 12 + date2.getUTCMonth();
    var month1 = date1.getUTCFullYear() * 12 + date1.getUTCMonth();
    return month2 - month1;
  }
  function dayDiff(date1, date2) {
    if(date2.getUTCMonth() === date1.getUTCMonth()){
      return date1.getUTCDate()-date2.getUTCDate();
    }
    return 0;
  }
  function getMonth(date) {
    return months[date.getUTCMonth()];
  }
  function displayDate() {
    if (date2.getTime() - date1.getTime() === 0) {
      return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate()) + ', ' + date1.getUTCFullYear()];
    }
    if (date1.getUTCMonth() === date2.getUTCMonth() && date1.getUTCFullYear() === date2.getUTCFullYear()) {
      return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate()), dateEnding(date2.getUTCDate())];
    }
    if (monthDiff(date1, date2) < 12 && date1.getUTCFullYear() !== date2.getUTCFullYear() ) {
      return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate()), getMonth(date2) + ' ' + dateEnding(date2.getUTCDate())];
    }
    if (monthDiff(date1, date2) <= 12 && dayDiff(date1, date2)>0) {
      return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate())+', '+date1.getUTCFullYear(), getMonth(date2) + ' ' + dateEnding(date2.getUTCDate())];
    }
    if (monthDiff(date1, date2) < 12) {
      return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate())+', '+date1.getUTCFullYear(), getMonth(date2) + ' ' + dateEnding(date2.getUTCDate())];
    }
    return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate()) + ', ' + date1.getUTCFullYear(), getMonth(date2) + ' ' + dateEnding(date2.getUTCDate()) + ', ' + date2.getUTCFullYear()];
  }
  var date1 = convertDate(str[0]);
  var date2 = convertDate(str[1]);
  return displayDate();
}

Make a Person

简单的类操作,善用 this

var Person = function(firstAndLast) {
  var fullName = firstAndLast;
  this.getFirstName = function() {
    return fullName.split(" ")[0];
  };
  this.getLastName = function() {
    return fullName.split(" ")[1];
  };
  this.getFullName = function() {
    return fullName;
  };
  this.setFirstName = function(name) {
    fullName = name + " " + fullName.split(" ")[1];
  };
  this.setLastName = function(name) {
    fullName = fullName.split(" ")[0] + " " + name;
  };
  this.setFullName = function(name) {
    fullName = name;
  };
};

Map the Debris

返回轨道周期,使用 forEach,参数中使用了 object

function orbitalPeriod(arr) {
  var GM = 398600.4418;
  var earthRadius = 6367.4447;
  arr.forEach(function(item) {
    var tmp = Math.round(2 * Math.PI * Math.sqrt(Math.pow(earthRadius + item.avgAlt, 3) / GM));
    delete item.avgAlt;
    item.orbitalPeriod = tmp;
  });
  return arr;
}

Pairwise

从第一个数组里面所有任意两个数和为第二个,返回索引和

function pairwise(arr, arg) {
  var pairArr = arr.slice();
  return pairArr.reduce( function (a,b,index){
      var search = arg - b;
      if ( pairArr.indexOf(search) != -1 && pairArr.indexOf(search) != index ){
         var total = index + pairArr.indexOf(search);
         pairArr.splice(index,1,NaN);
         pairArr.splice(pairArr.indexOf(search),1,NaN);
         return a + total;
      }
      return a;
  },0);
}