0%

前端大厂10道经典面试题汇总

1、写一个 js 函数,实现对一个数字每 3 位加一个逗号,如输入 100000, 输出 100,000(不考虑负数,小数)—百度前端面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let arr = []
function main(num) {
if (num === null) return
let n = parseInt(num).toString()
s(n)
}

function s(num) {
if (num.length > 3) {
arr[arr.length] = num.slice(-3)
s(num.slice(0, -3))
} else {
arr[arr.length] = num
}
}
main(123456789)

console.log(arr.reverse().join(","))

解题思路
本题是 js 实现 number.toLocaleString()方法,面试题做了简化不考虑负数小数,此题主要是考数据类型及字符串操作,答案不唯一。

按现实思路解题,现实中添加千位分隔符是从后到前,每 3 位添加逗号,所以这里输入数据转换成字符串后,利用 slice 方法的输入负数参数从后取的特点,从后取三位数字保存在数组中,并把取剩后的数据递归重复取值,直到数据不足 3 位,把剩下一起存入数组中。

这时数组中按顺序保存从后到前的分割数据。实例中数组是[‘789’,’456’,’123’]。通过 reverse 方法倒序输出,并通过 join 方法添加逗号。

2、给定一个字符串,找出其中无重复字符的最长子字符串长度—字节跳动前端面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// var lengthOfLongestSubstring = function(s) {
// let n = s.length;
// let set = new Set();
// let ans = 0, i = 0, j = 0;
// while (i < n && j < n) {
// if (!set.has(s[j])) {
// set.add(s[j++]);
// ans = Math.max(ans, j - i);
// } else {
// set.delete(s[i++]);
// }
// }
// return ans;
// };

//时间复杂度:O(2n) = O(n)O(2n)=O(n),在最糟糕的情况下,每个字符将被 ii 和 jj 访问两次。
//空间复杂度:O(min(m, n))O(min(m,n)),与之前的方法相同。滑动窗口法需要 O(k)O(k) 的空间,其中 kk 表示 Set 的大小。而Set的大小取决于字符串 nn 的大小以及字符集/字母 mm 的大小。



function StrLen(str) {
let result = 1 //最终要返回的结果的初始值
let norepeatStr = '' //用于存放无重复字符串
let len = str.length
for (let i = 0; i < len; i++) {
//charAt()获取的是字符串索引对应的具体字符
let specStr = str.charAt(i)
//indexOf()查找的是某个字符第一次出现并返回这个索引值,若没有这个字符,返回-1
let index = norepeatStr.indexOf(specStr)
if (index === -1) {
//将遍历得到的字符(未重复)拼接在norepeatStr后面
norepeatStr = norepeatStr + specStr
result = result < norepeatStr.length ? norepeatStr.length : result
} else {
//若遇到重复的字符,那么将已出现在norepeatStr里的字符删除,并将新的(重复的添加到末尾)
norepeatStr = norepeatStr.substr(index + 1) + specStr
}
}
return result
}

console.log(StrLen(abbbcbd))

解题思路
这题的要点就是无重复字符的理解。首先字符串内字符位置是固定的,我们要采用顺序循环的方式解题,然后就是理解无重复字符的含义,把当前字符串分割,每个小分割内不能出现重复的字符。也就是说分割的字符串是不会互相叠加重复的,每当该段分割的下一个字符与该段分割内字符相同,当即重新开始分割字符。

所以解题时需要一个存储当前分割片段的对象,用来比较下一个字符。并取这个分割片段的长度,与每个分割片段的最大长度比较即可。本题主要考的是题面的理解,以及字符串方法的运用,需要熟练地运用才能快速解题。

3、实现超出整数存储范围的两个大正整数相加—腾讯前端面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function func() {
let a = '333333333333333333333333333'
let b = '9999999999999999999'
let n1 = a.length
let n2 = b.length
for (let i = 0; i < Math.max(n1, n2) - Math.min(n1, n2); i ++) {
if (n1 > n2) b = '0' + b
if (n2 > n1) a = '0' + a
}
a = a.split('').reverse()
b = b.split('').reverse()
//split()基于指定的分隔符将一个字符串分割成多个子字符串并将结果放在一个数组中
//reverse()反转数组项的顺序(加法计算顺序)
//现在a,b数组中存储着相同个数的大数字的逆顺序拆解

let n = Math.max(n1, n2)
let result = Array.apply(this, Array(n)).map((item, i) => {
return 0
})
//生成一个长度为n的每个元素都为0的数组(用来保存最终结果)

for (let k = 0; k < n; k ++) {
let temp = parseInt(a[k]) + parseInt(b[k])
if (temp > 9) {
result[k] += temp - 10
result[k+1] = 1
} else {
result[k] += temp
}
}

//把ab数组中的数字相加减,注意进位

console.log(result.reverse().join('').toString())
//将数组项基于指定的分隔符以字符串输出

}

func()

解题思路
首先了解超出存储范围的大数字概念,每种数据类型可存储数据量都是存在范围的

数字类型的范围:

Number.MAX_VALUE = 1.7976931348623157e+308

Number.MIN_VALUE = 5e-324

整数类型的范围:-2-53-253

当超出这个范围,为了避免数据丢失,就要采用其他手段进行运算。在参考答案中,运用数组的方式解决这个问题。首先两个大整数要存储在数组中,要先保证位数对齐,我们比较字符串长度把低位数的大整数字符串前面添加相应的 0 占位, 并逆排序。创建一个新的数组保存运算结果,将两个大整数按从后到前的顺序进行相加减,这里注意进位。把得到的数组反转到正常顺序即可。

4、任意数组的全排列组合—阿里巴巴前端面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var arr = ['foo','bar','hello','world'];
var count = 1;
function getStr(a){
for (var i = 0; i < arr.length; i++) {
// indexOf 是es6数组的方法,如果不存在返回-1,存在返回下标
if(a.indexOf(arr[i])<0){

//数组 a 中不存在 arr[i],将arr[i]添加到数组末尾
a.push(arr[i]);

if(a.length==arr.length){
console.log(count++ + ': ' +a.join(""));
}else{
//结束一次for循环 进行了4次递归 getStr(['foo']) getStr(['bar']) getStr(['hello']) getStr(['world'])
getStr(a);

}
//一定从数组 a 中删除arr[i],进行下次循环,如果不删除就只能获得一种结果了
a.pop();
}
}
}
getStr([])

解题思路
本题做法不唯一,这里采用了把多项数组逐步两两相乘的方式,第一次先取二维数组前两项组合,把组合的结果在与第三项组合以此类推。这种递归做法简单易懂,把复杂的多项问题简化成两项问题的逐渐递增。

5、公司最近新研发了一种产品,共生产了 n 件。有 m 个客户想购买此产品,已知每个顾客出价。为了确保公平,公司决定要以一个固定的价格出售产品。每一个出价不低于要价的客户将会得到产品(每人只买一个),余下的将会被拒绝购买。请你找出能让公司利润最大化的售价。—京东前端面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let n = 3
let m = 4
let arr = [2, 8, 10, 7]
let key = 0, max = 0
arr = arr.sort( (a, b) => {
return a - b //升序
})
if (n < m) {
arr = arr.slice(m - n) //截取出价高的人
}
for (let i = 0; i < arr.length; i ++) {
if (max < arr[i] * (arr.length - i)) {
max = arr[i] * (arr.length - i)
key = arr[i]
}
}

console.log(key)

解题思路
本题是京东的业务演变题,首先要理清思路。本题中,固定出价,以及出价低于产品的顾客会被拒绝购买是解题核心。

条件中已知产品总个数,顾客出价。这里有个小陷阱,会出现 N<M 供不应求的情况,要特殊考虑。依据题目,我们首先需要对顾客出价排序,这里按升序排列。当供不应求出现时,我们截取出价高的顾客。然后把每个顾客的出价当做最终售价循环,得出最大化利润下的售价。

6、计算出字符串中出现次数最多的字符是什么,出现了多少次?—华为前端面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let s = 'asdaaaaaad'
let count = 0, char = '' //count出现次数 char字符
let arr = [] // 储存去重后字符
function foo(str) { // 去重
return r = str.split("").filter(function (element, index, self) {
return self.indexOf(element) === index // 输出第一次出现的字符
})
}

arr = foo(s)
for (let i = 0; i < arr.length; i ++) {
let n = (s.split(arr[i])).length - 1 //出现次数
if (count < n) {
count = n
char = arr[i]
}
}
console.log("count:" + count + ",char:" + char)

解题思路
计算出全部字符出现次数,并留下最大的。首先利用 filter()与 indexOf()的方法连用字符串去重,再将得到的作为索引,利用 split()分割字符串,得到字符出现次数,比较得出结果。

7、”123456789876543212345678987654321…”的第 n 位是什么?—小米面试题

1
2
3
4
5
let k = "1234567898765432"  //最小循环节
function getNum(n) {
console.log(k.charAt(n % k.length - 1))
}
getNum(20)

解题思路
这道题的答案不唯一,这里可以利用数学中最小循环节的概念解题,找到最小循环节后,利用余数查找第 n 位数字。

8、请编写一个 JavaScript 函数 parseQueryString,它的用途是把 URL 参数解析为一个对象—淘宝面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function parseQueryString(url) {
var pos = url.indexOf("?")
var obj = {}
if (pos != -1) {
var urlString=url.slice(pos+1)
var urlArr = urlString.split("&")
var keyValue = []
for (var i = 0; i < urlArr.length; i++) {
keyValue = urlArr[i].split("=")
obj[keyValue[0]]=keyValue[1]
}
}
return obj
}

解题思路
淘宝这道题是很常用的场景题,这里需要处理好分段次序,首先把?分离,然后按&分割最后按=分割,主要考察字符串的函数运用以及对象的创建。

9、如果给定的字符串是回文,返回 true,反之,返回 false。回文:如果一个字符串忽略标点符号、大小写和空格,正着读和反着读一模一样,那么这个字符串就是 palindrome(回文)。—网易前端面试题

1
2
3
4
5
6
7
8
function palindrome(str) {
let str1 = str.replace(/[^0-9a-zA-Z]/g,"").toLowerCase() // 去掉标点符号,转化成小写,比较参数一
let str2 = str1.split("").reverse().join("") // 翻转字符串,比较参数二
if (str1 === str2) {
return true
} else return false
}
console.log(palindrome("aBc,./1d42--==EFG0 00 h0 ';00gfE' ./.24d 1cBA")) // 输出结果:true

解题思路
去掉字符串多余的标点符号和空格,然后把字符串转化成小写来验证此字符串是否为回文。

10、确保字符串的每个单词首字母都大写,其余部分小写。——搜狐前端面试题

1
2
3
4
5
6
7
8
9
function titleCase(str) {
let aStr = str.toLowerCase().split(" ") // 转小写,分割成字符串数组
for (let i = 0; i < aStr.length; i ++) {
aStr[i] = aStr[i][0].toUpperCase() + aStr[i].slice(1) // 重新组合字符串元素
}
let oString = aStr.join(" ") //转成字符串
return oString
}
console.log(titleCase("I'm a title Case")) // 输出结果为 I'm A Title Case

解题思路
字符串转化成小写;
分割成字符串数组;
新组合字符串元素=首字母转大写+其余小写。

代码 github 地址