牛客网前端算法题记录
1、修改this指向:封装函数 f,使 f 的 this 指向指定的对象
//解法1:apply function bindThis(f, oTarget) { return function(){ return f.apply(oTarget,arguments); } } //解法2:bind function bindThis(f, oTarget) { return f.bind(oTarget); } //解法3:call function bindThis(f, oTarget) { return function(x,y){ return f.call(oTarget,x,y); }; }
2、获取 url 中的参数
1. 指定参数名称,返回该参数的值 或者 空字符串
2. 不指定参数名称,返回全部的参数对象 或者 {}
3. 如果存在多个同名参数,则返回数组
输入:http://www.nowcoder.com?key=1&key=2&key=3&test=4#hehe key
输出:[1, 2, 3]
function getUrlParam(sUrl, sKey) { var result = {}; sUrl.replace(/\??(\w+)=(\w+)&/g, function (a,b, c) { if (result[b]) { result[b] = [].concat(result[b], c); } else { result[b] = c; } }); if (sKey) { return result[sKey] || ''; } else { return result; } }
3、dom节点查找,查找两个节点的最近的一个共同父节点,可以包括节点自身
输入:oNode1 和 oNode2 在同一文档中,且不会为相同的节点
function commonParentNode(oNode1, oNode2) { for(;oNode1;oNode1=oNode1.parentNode){ if(oNode1.contains(oNode2)){ return oNode1; } } }
4、根据包名,在指定空间中创建对象
输入:namespace({a: {test: 1, b: 2}}, 'a.b.c.d')
输出:{a: {test: 1, b: {c: {d: {}}}}}
function namespace(oNamespace, sPackage) { var package = sPackage.split('.'); var obj = oNamespace; for(var i=0;i<package.length;i++){ if(typeof obj[package[i]]!=='object'){ obj[package[i]]={}; } obj=obj[package[i]]; } return oNamespace; }
5、数组去重,为 Array 对象添加一个去除重复项的方法
输入:[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a', 'a', NaN]
输出:[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a']
//解法1: Array.prototype.uniq = function () { return [...new Set(this)]; } //解法2: Array.prototype.uniq = function () { var resArr = []; var flag = true; for(var i=0;i<this.length;i++){ if(resArr.indexOf(this[i]) == -1){ if(this[i] != this[i]){ //排除 NaN if(flag){ resArr.push(this[i]); flag = false; } }else{ resArr.push(this[i]); } } } return resArr; }
6、斐波那契数列,用 JavaScript 实现斐波那契数列函数,返回第n个斐波那契数。 f(1) = 1, f(2) = 1 等
//解法1: function fibonacci(n) { if(n==1||n==2) return 1; return fibonacci(n-1)+fibonacci(n-2); } //解法2: function fibonacci(n) { //一、递归解法 //return n<=2?1:fibonacci(n-1)+fibonacci(n-2); //二、循环解法 var num1=1; var num2=1; for(var i=2;i<n;i++){ num2+=num1; num1=num2-num1; } return num2; }
7、时间格式化输出,按所给的时间格式输出指定的时间,格式说明:对于 2014.09.05 13:14:20
yyyy: 年份,2014
yy: 年份,14
MM: 月份,补满两位,09
M: 月份, 9
dd: 日期,补满两位,05
d: 日期, 5
HH: 24制小时,补满两位,13
H: 24制小时,13
hh: 12制小时,补满两位,01
h: 12制小时,1
mm: 分钟,补满两位,14
m: 分钟,14
ss: 秒,补满两位,20
s: 秒,20
w: 星期,为 ['日', '一', '二', '三', '四', '五', '六'] 中的某一个,本 demo 结果为 五
function formatDate(t,str){ var obj = { yyyy:t.getFullYear(), yy:(""+ t.getFullYear()).slice(-2), M:t.getMonth()+1, MM:("0"+ (t.getMonth()+1)).slice(-2), d:t.getDate(), dd:("0" + t.getDate()).slice(-2), H:t.getHours(), HH:("0" + t.getHours()).slice(-2), h:t.getHours() % 12, hh:("0"+t.getHours() % 12).slice(-2), m:t.getMinutes(), mm:("0" + t.getMinutes()).slice(-2), s:t.getSeconds(), ss:("0" + t.getSeconds()).slice(-2), w:['日', '一', '二', '三', '四', '五', '六'][t.getDay()] }; return str.replace(/([a-z]+)/ig,function($1){return obj[$1]}); }
8、获取字符串长度,如果第二个参数 bUnicode255For1 === true,则所有字符长度为 1,否则如果字符 Unicode 编码 > 255 则长度为 2
输入:'hello world, 牛客', false
输出:17
//解法1: function strLength(s, bUnicode255For1) { let len = s.length if(bUnicode255For1 !== true){ for(let i in s){ if(s.charCodeAt(i)>255)len++ } } return len } //解法2: function strLength(s, bUnicode255For1) { let length = s.length if(!bUnicode255For1){ for(let i = 0,len = s.len;i<length;i++){ if(s.charCodeAt(i)>255){ length++ } } } return length } //解法3: function strLength(s, bUnicode255For1) { let size = s.length; if(bUnicode255For1) return size; for(let i = 0; i < s.length; i++) { if(s.charCodeAt(i) > 255) size++; } return size; }
9、邮箱字符串判断,判断输入是否是正确的邮箱格式
输入:邮箱字符串
输出:true表示格式正确
function isAvailableEmail(sEmail) { var reg=/^([\w+\.])+@\w+([.]\w+)+$/; return reg.test(sEmail); }
10、颜色字符串转换,将 rgb 颜色字符串转换为十六进制的形式,如 rgb(255, 255, 255) 转为 #ffffff
1. rgb 中每个 , 后面的空格数量不固定
2. 十六进制表达式使用六位小写字母
3. 如果输入不符合 rgb 格式,返回原始输入
输入:'rgb(255, 255, 255)'
输出:#ffffff
//解法1 function rgb2hex(sRGB) { return sRGB.replace(/^rgb\((\d+)\s*\,\s*(\d+)\s*\,\s*(\d+)\)$/g, function(a, r, g, b){ return '#' + hex(r) + hex(g) + hex(b); }); } function hex(n){ return n < 16 ? '0' + (+n).toString(16) : (+n).toString(16); } //解法2: function rgb2hex(sRGB) { return sRGB.replace(/^rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)$/,function($0,$1,$2,$3){ return '#'+toHex($1)+toHex($2)+toHex($3); }); } function toHex(str){ return ('0'+(+str).toString(16)).slice(-2); } //解法3: function rgb2hex(sRGB){ var result=['#']; if(!/rgb\(\d+(,\s*\d+){2}\)/.test(sRGB)){ return sRGB; } sRGB.replace(/\d+/g,function($0){ result.push(('0'+(+$0).toString(16)).slice(-2)); }); return result.join(''); }
11、字符串字符统计,统计字符串中每个字符的出现频率,返回一个 Object,key 为统计字符,value 为出现频率
1. 不限制 key 的顺序
2. 输入的字符串参数不会为空
3. 忽略空白字符
输入:'hello world'
输出:{h: 1, e: 1, l: 3, o: 2, w: 1, r: 1, d: 1}
//解法1: function count(str) { var obj = {}; str.replace(/\S/g,function(s){ !obj[s]?obj[s]=1:obj[s]++; }) return obj; } //解法2: function count(str) { for(var i=0, m=str.length, res={}; i<m; i++){ if(str.charAt(i) in res) res[str.charAt(i)]++; else res[str.charAt(i)] = 1; } return res; } //解法3: function count(str) { var obj = {}; for (var i = 0; i < str.length; ++i) { if (str[i] !== ' ') { obj[str[i]] = obj[str[i]] ? ++obj[str[i]] : 1; } } return obj; }
12、将字符串转换为驼峰格式,css 中经常有类似 background-image 这种通过 - 连接的字符,通过 javascript 设置样式的时候需要将这种样式转换成 backgroundImage 驼峰格式,请完成此转换功能
1. 以 - 为分隔符,将第二个起的非空单词首字母转为大写
2. -webkit-border-image 转换后的结果为 webkitBorderImage
输入:'font-size'
输出:fontSize
//解法1: function cssStyle2DomStyle(sName) { return sName.replace(/\-[a-z]/g , function(a, b){ return b == 0 ? a.replace('-','') : a.replace('-','').toUpperCase(); }); } //解法2: function cssStyle2DomStyle(sName) { var arr = sName.split(''); //判断第一个是不是 '-',是的话就删除 if(arr.indexOf('-') == 0) arr.splice(0,1); //处理剩余的'-' for(var i=0; i<arr.length; i++){ if(arr[i] == '-'){ arr.splice(i,1); arr[i] = arr[i].toUpperCase(); } } return arr.join(''); } //解法3: function cssStyle2DomStyle(sName) { //使用正则将 前一位有-的字符替换为大写【-([a-z])】 //replace第二个参数为函数时: //函数的第一个参数是匹配模式的字符 【t】 //接下来的参数是与模式中的子表达式匹配的字符,可以有0个或多个这样的参数。【m】 //接下来的参数是一个整数,代表匹配在被替换字符中出现的位置【i】 //最后一个参数是被替换字符本身【这里没有用到】 return sName.replace(/-([a-z])/g,function(t,m,i){return i?m.toUpperCase():m;}) }
13、字符串字符统计,统计字符串中每个字符的出现频率,返回一个 Object,key 为统计字符,value 为出现频率
1. 不限制 key 的顺序
2. 输入的字符串参数不会为空
3. 忽略空白字符
输入:'hello world'
输出:{h: 1, e: 1, l: 3, o: 2, w: 1, r: 1, d: 1}
//解法1: function count(str) { var obj = {}; str.replace(/\S/g,function(s){ !obj[s]?obj[s]=1:obj[s]++; }) return obj; } //解法2: function count(str) { for(var i=0, m=str.length, res={}; i<m; i++){ if(str.charAt(i) in res) res[str.charAt(i)]++; else res[str.charAt(i)] = 1; } return res; } //解法3: function count(str) { return str.split('').reduce(function(prev, item) { if (item !== ' ') { prev[item] ? prev[item]++ : prev[item] = 1; } return prev; }, {}); }
14、查找数组元素位置,找出元素 item 在给定数组 arr 中的位置,如果数组中存在 item,则返回元素在数组中的位置,否则返回 -1
输入:[ 1, 2, 3, 4 ], 3
输出:2
//解法1:indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。 function indexOf(arr,item){ return arr.indexOf(item); } //解法2: function indexOf(arr, item){ var index = -1; arr.forEach(function(res,i){ if(res === item && index === -1){ index = i; } }); return index; }; //解法3: function indexOf(arr, item) { if (Array.prototype.indexOf){ return arr.indexOf(item); } else { for (var i = 0; i < arr.length; i++){ if (arr[i] === item){ return i; } } } return -1; }
15、数组求和,计算给定数组 arr 中所有元素的总和,数组中的元素均为 Number 类型
输入:[ 1, 2, 3, 4 ]
输出:10
//解法1:递归 function sum(arr) { var len = arr.length; if(len == 0){ return 0; } else if (len == 1){ return arr[0]; } else { return arr[0] + sum(arr.slice(1)); } } //解法2:循环添加 function sum(arr) { var s = 0; for (var i=arr.length-1; i>=0; i--) { s += arr[i]; } return s; } //解法3:reduce function sum(arr) { return arr.reduce(function(prev, curr, idx, arr){ return prev + curr; }); } //解法4:forEach function sum(arr) { var s = 0; arr.forEach(function(val, idx, arr) { s += val; }, 0); return s; }; //解法5:eval function sum(arr) { return eval(arr.join("+")); };
16、移除数组中的元素,移除数组 arr 中的所有值与 item 相等的元素。不要直接修改数组 arr,结果返回新的数组
输入:[1, 2, 3, 4, 2], 2
输出:[1, 3, 4]
//解法1:splice function remove(arr,item){ var newarr = arr.slice(0); for(var i=0;i<newarr.length;i++){ if(newarr[i] == item){ newarr.splice(i,1); i--; } } return newarr; } //解法2:push function remove(arr,item){ var newarr = []; for(var i=0;i<arr.length;i++){ if(arr[i] != item){ newarr.push(arr[i]); } } return newarr; } //解法3:Array function remove(arr,item){ return arr.filter(function(ele){ return ele != item; }) } //解法4:正则 function remove(arr, item) { return arr.filter(function (elem) { return elem !== item; }); }
17、移除数组中的元素,移除数组 arr 中的所有值与 item 相等的元素,直接在给定的 arr 数组上进行操作,并将结果返回
输入:[1, 2, 2, 3, 4, 2, 2], 2
输出:[1, 3, 4]
//解法1:把数组看成是队列,等于item元素直接删除,不等于的,先push再删除 function removeWithoutCopy(arr, item) { var n=arr.length; for(var i=0;i<n;i++){ if(arr[0]!==item) arr.push(arr[0]); arr.shift(); } return arr; } //解法2:while function removeWithoutCopy(arr, item) { while(arr.indexOf(item) != -1){ arr.splice(arr.indexOf(item),1); } return arr; } //解法3:splice function removeWithoutCopy(arr, item) { for(var i = 0; i < arr.length; i++){ if(arr[i] == item){ //splice方***改变数组长度,当减掉一个元素后,后面的元素都会前移,因此需要相应减少i的值 arr.splice(i,1); i--; } } return arr; } //解法4:set function removeWithoutCopy(arr, item) { arr=Array.from(new Set(arr)); var a=arr.indexOf(item); arr.splice(a,1); return arr; }
18、添加元素,在数组 arr 末尾添加元素 item。不要直接修改数组 arr,结果返回新的数组
输入:[1, 2, 3, 4], 10
输出:[1, 2, 3, 4, 10]
//解法1:slice function append(arr, item) { var newArr = arr.slice(0); // slice(start, end)浅拷贝数组 newArr.push(item); return newArr; } //解法2:concat function append(arr, item) { return arr.concat(item); } //解法3:... function append(arr, item) { return [...arr,item] }
19、删除数组最后一个元素,删除数组 arr 最后一个元素。不要直接修改数组 arr,结果返回新的数组
输入:[1, 2, 3, 4]
输出:[1, 2, 3]
//解法1:pop function truncate(arr) { var a = arr.slice(0); a.pop(); return a; } //解法2:slice function truncate(arr) { return arr.slice(0,-1); } //解法3:filter function truncate(arr) { return arr.filter(function(v,i,ar) { return i!==ar.length-1; }); } //解法4:迭代拷贝 function truncate(arr, item) { var newArr=[]; for(var i=0;i<arr.length-1;i++){ newArr.push(arr[i]); } return newArr; }
20、添加元素,在数组 arr 开头添加元素 item。不要直接修改数组 arr,结果返回新的数组
输入:[1, 2, 3, 4], 10
输出:[10, 1, 2, 3, 4]
//解法1:concat function prepend(arr, item) { return [item].concat(arr); } //解法2:push.apply function prepend(arr, item) { var newArr=[item]; [].push.apply(newArr, arr); return newArr; } //解法3:slice+unshift/splice function prepend(arr, item) { var newArr=arr.slice(0); newArr.unshift(item);//newArr.splice(0,0,item); return newArr; } //解法4:使用join+split+unshift/splice组合 function prepend(arr, item) { var newArr=arr.join().split(','); newArr.unshift(item);//newArr.splice(0,0,item); return newArr; } //解法5普通的迭代拷贝 function prepend(arr, item) { var newArr=[]; for(var i=0;i<arr.length;i++){ newArr.push(arr[i]); } newArr.unshift(item); return newArr; }
21、删除数组第一个元素。不要直接修改数组 arr,结果返回新的数组
输入:[1, 2, 3, 4]
输出:[2, 3, 4]
//解法1:slice function curtail(arr) { return arr.slice(1); } //解法2:filter function curtail(arr) { return arr.filter(function(v,i) { return i!==0; }); } //解法3:push.apply+shift function curtail(arr) { var newArr=[]; [].push.apply(newArr, arr); newArr.shift(); return newArr; } //解法4:concat+shift function curtail(arr) { var newArr = arr.concat(); newArr.shift(); return newArr; } //解法5:普通的迭代拷贝 function curtail(arr) { var newArr=[]; for(var i=1;i<arr.length;i++){ newArr.push(arr[i]); } return newArr; }
22、数组合并,合并数组 arr1 和数组 arr2。不要直接修改数组 arr,结果返回新的数组
输入:[1, 2, 3, 4], ['a', 'b', 'c', 1]
输出:[1, 2, 3, 4, 'a', 'b', 'c', 1]
//解法1:concat function concat(arr1, arr2) { return arr1.concat(arr2); } //解法2:slice+push.apply function concat(arr1, arr2) { var newArr=arr1.slice(0); [].push.apply(newArr, arr2); return newArr; } //解法3:slice+push function concat(arr1, arr2) { var newArr=arr1.slice(0); for(var i=0;i<arr2.length;i++){ newArr.push(arr2[i]); } return newArr; } //解法4:普通的迭代拷贝 function concat(arr1, arr2) { var newArr=[]; for(var i=0;i<arr1.length;i++){ newArr.push(arr1[i]); } for(var j=0;j<arr2.length;j++){ newArr.push(arr2[j]); } return newArr; }
23、添加元素,在数组 arr 的 index 处添加元素 item。不要直接修改数组 arr,结果返回新的数组
输入:[1, 2, 3, 4], 'z', 2
输出:[1, 2, 'z', 3, 4]
//解法1:slice+concat function insert(arr, item, index) { return arr.slice(0,index).concat(item,arr.slice(index)); } //解法2:concat +splice function insert(arr, item, index) { var newArr=arr.concat(); newArr.splice(index,0,item); return newArr; } //:解法3:slice+splice function insert(arr, item, index) { var newArr=arr.slice(0); newArr.splice(index,0,item); return newArr; } //解法4:普通的迭代拷贝 function insert(arr, item, index) { var newArr=[]; for(var i=0;i<arr.length;i++){ newArr.push(arr[i]); } newArr.splice(index,0,item); return newArr; }
24、计数,统计数组 arr 中值等于 item 的元素出现的次数
输入:[1, 2, 4, 4, 3, 4, 3], 4
输出:3
//解法1:filter function count(arr, item) { var count = arr.filter(function(a) { return a === item; //返回true的项组成的数组 }); return count.length; } //解法2:map function count(arr, item) { var count = 0; arr.map(function(a) { if(a === item) { count++; } }); return count; } //解法3:reduce function count(arr, item) { var count = arr.reduce(function(prev, curr) { return curr === item ? prev+1 : prev; }, 0); return count; } //解法4:forEach function count(arr, item) { var count = 0; arr.forEach(function(a) { a === item ? count++ : 0; }); return count; }
25、查找重复元素,找出数组 arr 中重复出现过的元素
输入:[1, 2, 4, 4, 3, 3, 1, 5, 3]
输出:[1, 3, 4]
//解法1:forEach function duplicates(arr) { var result = []; arr.forEach(function(elem){ if(arr.indexOf(elem) !=arr.lastIndexOf(elem) && result.indexOf(elem) == -1){ result.push(elem); } }); return result; } //解法2:filter function duplicates(arr) { return arr.filter(function(item,index,array){ return array.indexOf(item) !== array.lastIndexOf(item) && array.indexOf(item) === index; }) }
26、求二次方,为数组 arr 中的每个元素求二次方。不要直接修改数组 arr,结果返回新的数组
输入:[1, 2, 3, 4]
输出:[1, 4, 9, 16]
//解法1:map + 箭头函数 function square(arr) { return arr.map(a => a*a); } //解法2:forEach+push function square(arr) { var a = []; arr.forEach(function(e){ a.push(e*e); }); return a; } //解法3:map+数学函数 function square(arr) { return arr.map(e => Math.pow(e,2)); }
27、查找元素位置,在数组 arr 中,查找值与 item 相等的元素出现的所有位置
输入:['a','b','c','d','e','f','a','b','c'] 'a'
输出:[0, 6]
//解法1:forEach function findAllOccurrences(arr, target) { var temp = []; arr.forEach(function(val,index){ val !== target || temp.push(index); }); return temp; } //解法2:filter function findAllOccurrences(arr, target) { var result=[]; arr.filter(function(item,index){ return item===target&&result.push(index); }); return result; } //解法3:indexOf function findAllOccurrences(arr, target) { var result=[],index=arr.indexOf(target); while(index>-1){ result.push(index); index=arr.indexOf(target,index+1); } return result; } //解法4:forEach + 箭头函数 function findAllOccurrences(arr, target) { res = []; arr.forEach((e, i) => target == e ? res.push(i): res); return res; }
28、计时器,实现一个打点计时器,要求
1、从 start 到 end(包含 start 和 end),每隔 100 毫秒 console.log 一个数字,每次数字增幅为 1
2、返回的对象中需要包含一个 cancel 方法,用于停止定时操作
3、第一个数需要立即输出
//解法1:setTimeout function count(start, end) { console.log(start) var dingshi = setInterval(function () { if (start < end) { console.log(start += 1) } }, 100) return { cancel: function () { clearInterval(dingshi)} } } //解法2:setInterval function count(start, end) { //立即输出第一个值 console.log(start++); var timer = setInterval(function(){ if(start <= end){ console.log(start++); }else{ clearInterval(timer); } },100); //返回一个对象 return { cancel : function(){ clearInterval(timer); } }; }
29、流程控制,实现 fizzBuzz 函数,参数 num 与返回值的关系如下:
1、如果 num 能同时被 3 和 5 整除,返回字符串 fizzbuzz
2、如果 num 能被 3 整除,返回字符串 fizz
3、如果 num 能被 5 整除,返回字符串 buzz
4、如果参数为空或者不是 Number 类型,返回 false
5、其余情况,返回参数 num
输入:15
输出:fizzbuzz
//解法1: function fizzBuzz(num) { if(isNaN(num)){ return false; } var str=''; if(num%3===0){ str+='fizz'; } if(num%5===0){ str+='buzz'; } return str||num; } //解法2: function fizzBuzz(num) { if(typeof num != 'number'){ return false; }else{ return (num%3 == 0 && num%5 ==0) ? 'fizzbuzz' :(num%3 == 0 ? 'fizz' :(num%5 == 0 ? 'buzz':num)); } }
30、函数传参,将数组 arr 中的元素作为调用函数 fn 的参数
输入:function (greeting, name, punctuation) {return greeting + ', ' + name + (punctuation || '!');}, ['Hello', 'Ellie', '!']
输出:Hello, Ellie!
//解法1:... function argsAsArray(fn, arr) { return fn(...arr) } //解法2:apply function argsAsArray(fn, arr) { return fn.apply(this,arr); } //解法3:call function argsAsArray(fn, arr) { return fn.call(this, ...arr); } //解法4:bind function argsAsArray(fn, arr) { return fn.bind(this, ...arr)(); }
31、函数的上下文,将函数 fn 的执行上下文改为 obj 对象
输入:function () {return this.greeting + ', ' + this.name + '!!!';}, {greeting: 'Hello', name: 'Rebecca'}
输出:Hello, Rebecca!!!
//apply function speak(fn, obj) { return fn.apply(obj); } //call function speak(fn, obj) { return fn.call(obj); } //bind function speak(fn, obj) { return fn.bind(obj)(); }
32、返回函数,实现函数 functionFunction,调用之后满足如下条件:
1、返回值为一个函数 f
2、调用返回的函数 f,返回值为按照调用顺序的参数拼接,拼接字符为英文逗号加一个空格,即 ', '
3、所有函数的参数数量为 1,且均为 String 类型
输入:functionFunction('Hello')('world')
输出:Hello, world
//解法1: function functionFunction(str) { var f = function(s){ return str+", "+s; } return f; } //解法2: function functionFunction(str) { return function(anotherStr){ return str+', '+anotherStr; } } //解法3: function functionFunction(str) { var args = Array.prototype.slice.call(arguments) return function(){ return args.concat(Array.prototype.slice.call(arguments)).join(', ') } }
33、使用闭包,实现函数 makeClosures,调用之后满足如下条件:
1、返回一个函数数组 result,长度与 arr 相同
2、运行 result 中第 i 个函数,即 result[i](),结果与 fn(arr[i]) 相同
输入:[1, 2, 3], function (x) { return x * x;}
输出:4
//解法1: function makeClosures(arr, fn) { var result=new Array(); for(i=0;i<arr.length;i++){ result[i]=function(num){ return function(){ return fn(num) } }(arr[i]); } return result; } //解法2: function makeClosures(arr, fn) { let result = [] for(let i = 0;i < arr.length; i++) { result[i] = fn.bind(this,arr[i]) } return result } //解法3: function makeClosures(arr, fn) { const newArr = [] arr.forEach(item=>{ newArr.push(()=>{ return fn(item) }) }) return newArr }
34、二次封装函数,已知函数 fn 执行需要 3 个参数。请实现函数 partial,调用之后满足如下条件:
1、返回一个函数 result,该函数接受一个参数
2、执行 result(str3) ,返回的结果与 fn(str1, str2, str3) 一致
输入:var sayIt = function(greeting, name, punctuation) { return greeting + ', ' + name + (punctuation || '!'); }; partial(sayIt, 'Hello', 'Ellie')('!!!');
输出:Hello, Ellie!!!
// call function partial(fn, str1, str2) { function result(str3) { return fn.call(this, str1, str2, str3); } return result; } // apply function partial(fn, str1, str2) { function result(str3) { return fn.apply(this, [str1, str2, str3]); } return result; } //bind function partial(fn, str1, str2) { return fn.bind(this, str1, str2); } //匿名函数 function partial(fn, str1, str2) { return function(str3) { return fn.call(this, str1, str2, str3); } }
35、使用arguments,函数 useArguments 可以接收 1 个及以上的参数。请实现函数 useArguments,返回所有调用参数相加后的结果。本题的测试参数全部为 Number 类型,不需考虑参数转换。
输入:1,2,3,4
输出:10
//解法1:slice call function useArguments() { var arr=Array.prototype.slice.call(arguments)//把arguments类数组转化为数组 return eval(arr.join("+"));//求和 } //解法2:reduce call function useArguments() { var result = Array.prototype.reduce.call(arguments,function(a,b){return a+b;}); return result; } //解法3:... function useArguments() { return [...arguments].reduce((a,b)=>a+b) }
36、使用apply调用函数,实现函数 callIt,调用之后满足如下条件
1、返回的结果为调用 fn 之后的结果
2、fn 的调用参数为 callIt 的第一个参数之后的全部参数
输入:无
输出:无
//解法1: function callIt(fn) { return fn.apply(this,[].slice.call(arguments,1)); } //解法2: function callIt(fn) { return fn.apply(this, [ ...arguments].slice(1)); }
37、柯里化,已知 fn 为一个预定义函数,实现函数 curryIt,调用之后满足如下条件:
1、返回一个函数 a,a 的 length 属性值为 1(即显式声明 a 接收一个参数)
2、调用 a 之后,返回一个函数 b, b 的 length 属性值为 1
3、调用 b 之后,返回一个函数 c, c 的 length 属性值为 1
4、调用 c 之后,返回的结果与调用 fn 的返回值一致
5、fn 的参数依次为函数 a, b, c 的调用参数
输入:var fn = function (a, b, c) {return a + b + c}; curryIt(fn)(1)(2)(3);
输出:6
//解法1: function curryIt(fn) { var arr=[],l = fn.length; return function(x){ arr.push(x); return arr.length < l ? arguments.callee : fn.apply(null,arr); } } //解法2: function curryIt(fn) { return (a)=>(b)=>(c)=>fn(a,b,c); }
38、模块,完成函数 createModule,调用之后满足如下要求:
1、返回一个对象
2、对象的 greeting 属性值等于 str1, name 属性值等于 str2
3、对象存在一个 sayIt 方法,该方法返回的字符串为 greeting属性值 + ', ' + name属性值
//解法1: function createModule(str1, str2) { var obj = { greeting : str1, name : str2, sayIt : function(){return this.greeting + ", " + this.name;} }; return obj; } //使用构造函数法 function createModule(str1, str2) { function Obj(){ this.greeting = str1; this.name = str2; this.sayIt = function(){ return this.greeting + ', ' + this.name; }; } return new Obj(); } //构造函数与原型组合 function createModule(str1, str2) { function CreateMod(){ this.greeting = str1; this.name = str2; } CreateMod.prototype.sayIt = function(){ return this.greeting + ', ' + this.name; } return new CreateMod(); }
39、二进制转换,获取数字 num 二进制形式第 bit 位的值。注意:
1、bit 从 1 开始
2、返回 0 或 1
3、举例:2 的二进制为 10,第 1 位为 0,第 2 位为 1
输入:128,8
输出:1
//解法1: function valueAtBit(num, bit) { return (num >> (bit -1)) & 1; } //解法2: function valueAtBit(num, bit) { //toString转化为二进制,split将二进制转化为数组,reverse()将数组颠倒顺序 var arr = num.toString(2).split("").reverse(); return arr[bit-1]; } //解法3: //用第bit位与的方法: function valueAtBit(num, bit) { var result = num & Math.pow(2, bit-1); return result? 1 : 0; }
40、二进制转换十进制
输入:'11000000'
输出:192
//解法1: function base10(str) { /** 其它进制转十进制 parseInt(str,2) parseInt(str,8) parseInt(str,16) */ return parseInt(str,2); } //解法2: function base10(str) { return str.split('').reduce(function(total,value,index){ return total + (value << (str.length - 1 - index)); },0); } //解法3: function base10(str) { return Number('0b'+str); }
41、二进制转换,将给定数字转换成二进制字符串。如果字符串长度不足 8 位,则在前面补 0 到满8位。
输入:65
输出:01000001
//解法1: function convertToBinary(num) { var str = num.toString(2); while(str.length < 8) { str = "0" + str; } return str; } //解法2: function convertToBinary(num) { var toBit = num.toString(2); return ( toBit / Math.pow(10, 8) ).toFixed(8).substr(2); } //解法3: function convertToBinary(num) { return ("00000000" + num.toString(2)).slice(-8) }
42、乘法,求 a 和 b 相乘的值,a 和 b 可能是小数,需要注意结果的精度问题
输入:3, 0.0001
输出:0.0003
//解法1: function multiply(a, b) { return parseFloat((a*b).toFixed(10)); } //解法2: function multiply(a, b) { return Number((a*b).toFixed(4)); }
43、改变上下文,将函数 fn 的执行上下文改为 obj,返回 fn 执行后的值
输入:alterContext(function() {return this.greeting + ', ' + this.name + '!'; }, {name: 'Rebecca', greeting: 'Yo' })
输出:Yo, Rebecca!
//解法1:bind function alterContext(fn, obj) { return fn.bind(obj)(); } //解法2:call function alterContext(fn, obj) { return fn.call(obj); } //解法3:apply function alterContext(fn, obj) { return fn.apply(obj); }
44、批量改变对象的属性,给定一个构造函数 constructor,请完成 alterObjects 方法,将 constructor 的所有实例的 greeting 属性指向给定的 greeting 变量。
输入:var C = function(name) {this.name = name; return this;}; var obj1 = new C('Rebecca');alterObjects(C, 'What\'s up'); obj1.greeting;
输出:What's up
//解法1: function alterObjects(constructor, greeting) { constructor.prototype.greeting = greeting; } //解法2: function alterObjects(constructor, greeting) { // 防止属性屏蔽失败 Object.defineProperty(constructor.prototype, "greeting", { value: greeting }); }
45、属性遍历,找出对象 obj 不在原型链上的属性(注意这题测试例子的冒号后面也有一个空格~)
1、返回数组,格式为 key: value
2、结果数组不要求顺序
输入:var C = function() {this.foo = 'bar'; this.baz = 'bim';}; C.prototype.bop = 'bip'; iterate(new C());
输出:["foo: bar", "baz: bim"]
//解法1: function iterate(obj) { return Object.getOwnPropertyNames(obj).map(function(key){ return key+": "+obj[key]; }); } //解法2: function iterate(obj) { return Object.keys(obj).map(function(key) { return key + ": " + obj[key]; }); }
46、一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
//解法1 function jumpFloor(number) { let a = 1 let b = 2 let i = 1 while(i++ < number){ [a,b] = [b,a+b] } return a } module.exports = { jumpFloor : jumpFloor }; //解法2 function jumpFloor(number) { // write code here let st1 = 1 let st2 = 2 if (number==1 || number==2) { return number } for (let i=2;i<number;i++) { let temp = st2 st2 +=st1 st1 = temp } return st2 } module.exports = { jumpFloor : jumpFloor };
47、链表中的节点每K个一组翻转,将给出的链表中的节点每\ k k 个一组翻转,返回翻转后的链表,如果链表中的节点数不是\ k k 的倍数,将最后剩下的节点保持原样,你不能更改节点中的值,只能更改节点本身。要求空间复杂度 O(1)
输入:{1,2,3,4,5},2
输出:{2,1,4,3,5}
//解法1: function reverseKGroup( head , k ) { let pre = null,cur = head; let p = head; //查找长度是否满足反转的数量 for(let i = 0;i<k;i++){ if(p == null) return head; p = p.next; } //对该k个链表元素进行反转 for(let j = 0;j<k;j++){ let temp = cur.next; cur.next = pre; pre = cur; cur = temp; } //反转后。head.next已经成为当前反转后链表的最后一个元素,它的指向将是下一个递归的开始点 //而此时pre已经是最后一个元素,cur是下一个范围的第一元素 head.next = reverseKGroup(cur,k); return pre; } module.exports = { reverseKGroup : reverseKGroup };
48、子数组的最大累加和问题,给定一个数组arr,返回子数组的最大累加和,例如,arr = [1, -2, 3, 5, -2, 6, -1],所有子数组中,[3, 5, -2, 6]可以累加出最大的和12,所以返回12,题目保证没有全为负数的数据,[要求]时间复杂度为O(n),空间复杂度为O(1)
输入:[1,-2,3,5,-2,6,-1]
输出:12
//解法1: function maxsumofSubarray( arr ) { // write code here var max=0; var value=0; for(var i=0;i<arr.length;i++){ if(value<0)value=0 value+=arr[i]; if(value>max) max=value; } return max; }
49、最长无重复子数组,给定一个数组arr,返回arr的最长无重复元素子数组的长度,无重复指的是所有数字都不相同。子数组是连续的,比如[1,3,5,7,9]的子数组有[1,3],[3,5,7]等等,但是[1,3,7]不是子数组
输入:[2,3,4,5] [2,2,3,4,3]
输出:4 3
//解法1 function maxLength( arr ) { var map = new Map(), max = 0, start = 0 for(let i = 0; i < arr.length; i++) { if(map.has(arr[i])) { start = Math.max(map.get(arr[i]) + 1 , start) } map.set(arr[i], i) max = Math.max(max, i - start + 1) } return max }
50、判断链表中是否有环,判断给定的链表中是否有环。如果有环则返回true,否则返回false。你能给出空间复杂度的解法么?
//解法1: function hasCycle( head ) { if (head == null) return false; let slow = head; let fast = head; while (fast && fast.next) { slow = slow.next; fast = fast.next.next; if (slow == fast) return true; } return false; } //解法2: function hasCycle( head ) { if (!head || !head.next) return false; let slow = head, fast = head.next.next; while (fast && fast.next){ if(slow === fast) return true; slow = slow.next; fast = fast.next.next; } return false; }
51、合并两个有序数组,给出两个有序的整数数组 和 ,请将数组 合并到数组 中,变成一个有序的数组,注意: 可以假设 数组有足够的空间存放 数组的元素, 和 中初始的元素数目分别为 和
function merge( A, m, B, n ) { if (n === 0) return A A.push(...B) return A.sort((a, b) => (a - b)) }
52、链表中环的入口节点,对于一个给定的链表,返回环的入口节点,如果没有环,返回null,你能给出不利用额外空间的解法么?
function detectCycle( head ) { while(head){ if(head.flag){ return head; } head.flag = true; head = head.next } }
53、括号序列,给出一个仅包含字符'(',')','{','}','['和']',的字符串,判断给出的字符串是否是合法的括号序列,括号必须以正确的顺序关闭,"()"和"()[]{}"都是合法的括号序列,但"(]"和"([)]"不合法。
输入:"[" "[ ]"
输出:false true
//解法1 function isValid( s ) { if (s.length % 2) return false; const queue = []; const map = { ']': '[', ')': '(', '}': '{', }; const left = Object.values(map); for (let i = 0; i < s.length; i++) { if (left.includes(s[i])) { queue.push(s[i]); } else { if (map[s[i]] !== queue.pop()) return false; } } return queue.length === 0; } //解法2 function isValid( s ) { var result = [] for(var i = 0 ; i < s.length ; i++){ //其实是对数组进步性判断 result.push(s[i]) if(result.slice(result.length -2).join("")=="()" || result.slice(result.length -2).join("")=="[]" ||result.slice(result.length -2).join("")=="{}"){ result.pop() result.pop() } } return result.length==0 }
54、删除链表的倒数第n个节点,给定一个链表,删除链表的倒数第 nn 个节点并返回链表的头指针,给出的链表为: 1\to 2\to 3\to 4\to 51→2→3→4→5, n= 2n=2,删除了链表的倒数第 nn 个节点之后,链表变为1\to 2\to 3\to 51→2→3→5.
输入:{1,2},2
输出:{2}
function removeNthFromEnd( head , n ) { let fast = head, slow = head; while(n--) { fast = fast.next; } if(fast == null) return head.next; while(fast.next) { fast = fast.next; slow = slow.next; } slow.next = slow.next.next; return head; }