愈来愈多的公司都在面试前加入了笔试环节。javascript
有的甚至会根据你的笔试答题状况来决定是否进入面试环节。html
固然,进入面试环节,也会时不时的出几道算法或者其余类型的相关的题目让你写出来。前端
因此不只要会说,还要会写。java
关于手写面试题的文章也有好多,实现 call,apply,bind 之类的,也都最好要掌握。python
总结了一些我在面试中遇到的题,这些题不像是基础部分,会针对特定的公司,也许不必定适合你,因为面试的级别不高,题目难度属于中等偏下
,因此仅供参考。jquery
若想提升算法思惟,建议多刷刷 leetcode
。git
一直在纠结这篇文章该不应分享出来,我的感受此类题目不像是基础知识,做用不大。可是为了兑现上篇文章,因此仍是水了这篇。github
// getPhone('13211223344') => 132****3344
function getPhone(phone) {
// todo
}
复制代码
方法有不少,循环遍历,或者转换成数组,使用数组的各类 api。面试
固然仍是正则比较简单。正则表达式
function getPhone(phone) {
phone = "" + phone;
const reg = /(\d{3})\d{4}(\d{4})/;
return phone.replace(reg, "$1****$2")
}
复制代码
若是学习正则的话,
推荐 老姚
JS正则表达式完整教程
这篇文章很长,需细嚼慢咽。
/* 将一维数组切分红二维数组: 按照第二个参数(数字)的值,来决定二维数组长度 */
let a = ['a', 'b', 'c', 'd'];
let result = chunk(a, 3);
console.log(result) => [['a', 'b', 'c'], ['d']];
let result = chunk(a, 2);
console.log(result) => [['a', 'b'], ['c','d']];
let result = chunk(a, 1);
console.log(result) => [['a'],['b'],['c'],['d']];
复制代码
提供其中一种解题方法,用数组的 slice 方法。
function chunk(arr, ind) {
const len = arr.length;
if(ind > len || ind <= 0) return [];
const newArr = [];
for(let i = 0;i < len;i += ind) {
let j = i;
newArr.push(arr.slice(j, j += ind));
}
return newArr;
}
复制代码
若是学习数组相关 api 的话,
推荐 OBKoro1
的文章 js 数组详细操做方法及解析合集
// let obj = {foo: {bar: {name:'biz'}}};
// get(obj,'foo.bar.name'); // biz
// obj = {};
// get(obj,'foo.bar.name'); // underfind
// get(obj,'foo.bar.name','biz'); // biz
function get(obj, path, defalutValue) {
// todo
}
复制代码
个人解法是利用了递归。
function get(obj, path, defalutValue) {
if (!path || JSON.stringify(obj) === '{}') {
return defalutValue;
}
for(let key in obj) {
if(!path.split('.').includes(key)) {
return null
}
if(!path.endsWith(key)) {
return typeof obj[key] === 'object' ? get(obj[key], path, defalutValue) : defalutValue;
}
return obj[key]
}
}
复制代码
关于递归用法的话,若是不清楚:
const data = {
rows: [
["Lisa", 16, "Female", "2000-12-01"],
["Bob", 22, "Male", "1996-01-21"]
],
metaData: [
{ name: "name", note: '' },
{ name: "age", note: '' },
{ name: "gender", note: '' },
{ name: "birthday", note: '' }
]
}
/* rows 是数据, metaData 是对数据的说明。 现写⼀个函数 parseData,将上⾯面的对象转化为指望的数组 */
outputData = [
{ name: "Lisa", age: 16, gender: "Female", birthday: "2000-12-01" },
{ name: "Bob", age: 22, gender: "Male", birthday: "1996-01-21" },
]
复制代码
使用了一种比较笨拙的方法,循环遍历
const parseData = (data) => {
const newData = [];
const { metaData, rows } = data;
const mapArr = metaData.map(item => item.name)
rows.forEach((item,ind) => {
const obj = {};
item.forEach((item,ind) => {
obj[[mapArr[ind]]]=item
})
newData.push(obj)
});
return newData;
}
复制代码
结合生活编码
利用了 promise 加 递归实现。
function delay(timer) {
return () => (
new Promise(resolve => {
setTimeout(resolve, timer);
})
)
}
const red = delay(10000);
const green = delay(5000);
const yellow = delay(2000);
const traffic = document.getElementById('traffic');
(function resStart(){
traffic.innerText = '红';
red().then(()=> {
traffic.innerText = '绿';
return green();
})
.then(()=> {
traffic.innerText = '黄';
return yellow();
})
.then(()=> {
return resStart();
})
})()
复制代码
其余解法能够参考这篇文章:
推荐 FreePotato
的文章 【javascript】一个能够配置的红绿灯
/* 按照调用实例,实现下面的Person方法 */
Person("Li");
// 输出: Hi! This is Li!
Person("Dan").sleep(10).eat("dinner");
// 输出:
// Hi! This is Dan!
// 等待10秒
// Wake up after 10
// Eat dinner~
Person("Jerry").eat("dinner").eat("supper");
// 输出:
// Hi This is Jerry!
// Eat dinner~
// Eat supper~
Person("Smith").sleepFirst(5).eat("supper");
// 输出:
// 等待5秒
// Wake up after 5
// Hi This is Smith!
// Eat supper
复制代码
链式调用能够阅读下 jquery 的源码,在每一个方法的最后 return this 就行了。
1.连接全部 this;
2.把全部任务放到任务队列里面;
3.经过一个方法有序执行队列里面的任务;
function Person(name) {
this.task = [];
const fn = () => {
console.log(`Hi! This is ${name}!`);
this.then();
}
this.task.push(fn);
setTimeout(()=>{
this.then();
})
return this;
}
Person.prototype = {
sleepFirst(timeout) {
const fn = () => {
console.log(`Wake up after ${timeout}`)
setTimeout(() => {
this.then();
}, timeout)
}
this.task.unshift(fn);
return this;
},
sleep(timeout) {
const fn = () => {
console.log(`Wake up after ${timeout}`)
setTimeout(() => {
this.then();
}, timeout)
}
this.task.push(fn);
return this;
},
eat(meal) {
const fn = () => {
console.log(`Eat ${meal}`)
this.then();
}
this.task.push(fn);
return this;
},
then() {
const fn = this.task.shift();
fn && fn();
}
}
复制代码
/* 下面的数据结构中,不一样层级的key可能会相同。 实现一个方法,调用时更新数组中的key值, 使所用的key对应的值更新为新的随机数, 而且保证更新前相同的key值更新后也相同。 */
let arr = [
{
key: 123123,
child: {
key: 3213313,
child: {
key: 123123,
child: {
key: 3213313
}
}
}
},
{
key: 789789,
child: {
key: 312308,
child: {
key: 123123,
child: {
key: 3213313,
child: {
key: 873432
}
}
}
}
}
]
function update(arr) {
// todo
}
复制代码
基本思路是:
function update(arr) {
const num = Math.ceil(Math.random()*1000000)/1000000;
for(let item of arr) {
item.key += num;
(function updateKey(obj){
if(!obj.hasOwnProperty('child')) {
return obj.key += num;
}
obj.child.key += num;
if(obj.hasOwnProperty('child')) {
return updateKey(obj.child);
}
})(item)
}
return arr;
}
复制代码
面试官提供的思路是转换字符串,而后利用正则匹配。若是有大佬写出来了,欢迎告知。
首先须要先了解什么是代码的复杂度,
复杂度是怎么计算的。
能够参考如下两篇文章,也可自行搜索,相关文章有不少:
从网上截取了一张关于排序的复杂度。如若侵权,告知删除。
在了解了基本的堆和队列之后,
这两种数据结构相互转换也是须要了解到的。
能够参考下面的文章,也可自行搜索,相关文章有不少。
首先得须要了解什么是链表,
链表和数组有什么区别。
能够参考下面的文章,也可自行搜索,相关文章有不少:
/* 输入:1 -> 2 -> 3 -> 4 输出:4 -> 3 -> 2 -> 1 */
复制代码
设置两个指针, cur 为当前节点, pre 为当前节点的前一个节点,
利用 pre 让节点反转所指方向。
reverseList(){
if(this.head == null) return null;
let cur = this.head;
let prev = null;
while( cur!==null ){
let nextNode = cur.next
cur.next = prev
prev = cur
cur = nextNode
}
this.head = prev
}
复制代码
还有一种经常使用的办法就是龟兔赛跑。利用两个快慢指针,慢指针每次走一格,快指针每次走两格,当快慢指针相遇的时候,就能够说明此单链表有环。
hasCircle() {
let fast = this.head;
let slow = this.head;
while (fast !== null && fast.next !== null) {
fast = fast.next.next
slow = slow.next
if (slow === fast) return true
}
return false
}
复制代码
/* 输入:4 -> 1 -> 3 -> 5 输出:1 -> 3 -> 4 -> 5 */
复制代码
这里使用了是快排
// 建立节点
class Node {
constructor(value) {
this.val = value;
this.next = null;
}
}
// 建立链表
class NodeList {
constructor(arr) {
let head = new Node(arr.shift());
let next = head;
arr.forEach(item => {
next.next = new Node(item);
next = next.next;
})
return head;
}
}
// 交换两个链表的值
function swap(p,q) {
let val = p.val;
p.val = q.val;
q.val = val;
}
// 找到基准值
function partion(begin,end) {
let val = begin.val;
let p = begin;
let q = begin.next;
while(q !== end) {
if(q.val < val) {
p = p.next;
swap(p, q) // 交换小的值
}
q = q.next; // 指针后移
}
swap(p, begin);// 交换基准值
return p;
}
// 利用快排递归
function sort(begin,end = null) {
if(begin !== end) {
let part = partion(begin,end);
// 两路快排
sort(begin,part);
sort(part.next,end);
}
return begin
}
复制代码
/* 输入:1 -> 2 -> 3 -> 4, 11 -> 21 -> 31 -> 41 输出:1 -> 2 -> 3 -> 4 -> 11 -> 21 -> 31 -> 41 */
复制代码
用递归的方法将两个单链表整合在一块儿
function merge(list1, list2) {
if (list1 == null) return list2;
else if (list2 == null) return list1;
let mergehead = null;
if (list1.val <= list2.val) {
mergehead = list1;
mergehead.next = merge(list1.next, list2);
} else {
mergehead = list2;
mergehead.next = this.merge(list1, list2.next);
}
return mergehead;
}
复制代码
更多关于链表的题目,
推荐 尾尾部落
的文章 [算法总结] 17 题搞定 BAT 面试——链表题
老生常谈的问题,习惯了使用 api 去重的童鞋要当心了,
有的面试官可能会刁难你,好比说,不使用 api,不能建立新的变量。
推荐 冴羽
的文章 - JavaScript专题之数组去重
// 给定 nums = [2, 7, 11, 15], target = 9
// 由于 nums[0] + nums[1] = 2 + 7 = 9
// 因此返回 [0, 1]
/** * @param {number[]} nums * @param {number} target * @return {number[]} */
var twoSum = function(nums, target) {
};
复制代码
此题解法也有不少种,
好比说 暴力解法,两层循环,时间复杂度较高。
能够把代码在 leetcode 运行一次,看看运行效率,而后寻找更优的解法。
下面的解法只是一种思路,不是最优的。
var twoSum = function(nums, target) {
const obj = {};
for(let i=0;i<nums.length;i++) {
const num = nums[i];
if(obj[num] === undefined) {
const mins = target - num;
obj[mins] = i;
} else {
return [obj[num], i]
}
}
return [];
};
复制代码
// 输入: [-2,1,-3,4,-1,2,1,-5,4],
// 输出: 6
// 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
/** * @param {number[]} nums * @return {number} */
var maxSubArray = function(nums) {
}
复制代码
提供一种解题思路
var maxSubArray = function(nums) {
let current = nums[0];
let sum = nums[0];
for (let i = 1; i < nums.length; i++) {
if (current > 0) {
current += nums[i];
} else {
current = nums[i];
}
if (current > sum) {
sum = current;
}
}
return sum;
}
复制代码
// 输入: nums1 = [1,2,2,1], nums2 = [2,2]
// 输出: [2]
/** * @param {number[]} nums1 * @param {number[]} nums2 * @return {number[]} */
var intersection = function(nums1, nums2) {
}
复制代码
两个数组中查找重复出现的值,且只能出现一次。
var intersection = function(nums1, nums2) {
const map = {};
const res = [];
nums1.forEach((item) => {
map[item] = 1;
});
nums2.forEach((item) => {
if (map[item] === 1) {
res.push(item);
map[item]++;
}
});
return res;
}
复制代码
这也是常考的一道题。
若是你挥毫泼墨写出了阮老师版本的快排后就要当心了。
此写法虽然简单易理解,可是时间复杂度和空间复杂度仍是比较高的,
不适合大数据排序。
优化版本的三路快排,时间复杂度仍是没有下降。
从性能来讲,仍是推荐 分治法
。
推荐 百度-舞动乾坤
的文章JS快速排序&三路快排
或者 张晨辉Allen
的 java 版本文章 快速排序算法原理及实现
推荐 我自建群里的一位小哥的 github leet-code-solutions
python,go,js 多语言知足你。
水平有限,若是有不妥的回答,还望指出,谢谢。
自认很菜,建立了一个数据结构和算法的交流群,不限开发语言,前端后端,欢迎各位同窗入驻。
群以超过扫码限制人数,能够加我好友,邀请你入群。
请备注:掘金加群
哦