导读:在本系列文章中,我整理了大量常见前端面试的手写体,以及详细的答案和代码示例。
如果需要更多完整版笔记或者前端资料,请看置顶文章评论区,无偿分享给大家
1.JavaScript面试真题 (35题)
2.CSS面试真题(20题)
3.ES6面试真题 (10题)
4.Vue2面试真题 (29题)
5.Vue3面试真题 (6题)
6.React面试真题 (31题)
7.Node.js面试真题 (14题)
8.小程序面试真题 (8题)
9.HTTP面试真题 (14题)
10.Typescript面试真题 (12题)
。。。。。。。。。。。。。。。。。。
手写题:
- 字节
1.1 两数之和
给定⼀个整数数组nums 和⼀个⽬标值target ,请你在该数组中找出和为⽬标值的那两个整数,并返回他们的数组下标。
你可以假设每种输⼊只会对应⼀个答案。但是,你不能重复利⽤这个数组中同样的元素。
⽰例:
ini 代码解读复制代码给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9所以返回 [0, 1]
解题思路:
• 初始化⼀个map = new Map()
• 从第⼀个元素开始遍历nums
• 获取⽬标值与nums[i] 的差值,即k = target - nums[i] ,判断差值在map 中是否存在
◦ 不存在(map.has(k) 为false ),则将nums[i] 加⼊到map 中(key为nums[i] ,value为i ,⽅便查找map中是否存在某值,并可以通过get ⽅法直接拿到下标)
◦ 存在(map.has(k) ),返回[map.get(k), i] ,求解结束
• 遍历结束,则nums 中没有符合条件的两个数,返回[]
时间复杂度:O(n)
代码实现:
ini 代码解读复制代码const twoSum = function(nums, target) {
let map = new Map()
for(let i = 0; i< nums.length; i++) {
let k = target-nums[i]
if(map.has(k)) {
return [map.get(k), i]
}
map.set(nums[i], i)
}
return [];
};
1.2 N数之和
请⽤算法实现,从给定的⽆需、不重复的数组A中,取出N个数,使其相加和为M。并给出算法的时间、空间复杂度,如:
ini 代码解读复制代码var arr = [1, 4, 7, 11, 9, 8, 10, 6];
var N = 3;
var M = 27;
Result:
[7, 11, 9], [11, 10, 6], [9, 8, 10]
解题思路:利⽤⼆进制
根据数组⻓度构建⼆进制数据,再选择其中满⾜条件的数据。
我们⽤1 和0 来表⽰数组中某位元素是否被选中。因此,可以⽤0110 来表⽰数组中第1 位和第2 位被选中了。 所以,本题可以解读为:
• 数组中被选中的个数是N 。
• 被选中的和是M 。
我们的算法思路逐渐清晰起来:遍历所有⼆进制,判断选中个数是否为N ,然后再求对应的元素之和,看其是否为M 。
- 从数组中取出N个数
例如:
ini 代码解读复制代码var arr = [1, 2, 3, 4];
var N = 3;
var M = 6;
如何判断N=3 是,对应的选取⼆进制中有⼏个1 喃?
最简单的⽅式就是:
dart 代码解读复制代码const n = num => num.toString(2).replace(/0/g, '').length
这⾥我们尝试使⽤⼀下位运算来解决本题,因为位运算是不需要编译的(位运算直接⽤⼆进制进⾏表⽰,省去了中间过程的各种复杂转换,提⾼了速度),肯定速度最快。
我们知道1&0=0 、1&1=1 , 1111&1110=1110 ,即15&14=14 ,所以我们每次& ⽐⾃⾝⼩1 的数都会消除⼀个1 ,所以这⾥建⽴⼀个迭代,通过统计消除的次数,就能确定最终有⼏个1了:
dart 代码解读复制代码const n = num => {
let count = 0
while(num) {
num &= (num - 1)
count++
}
return count
}
2. 和为M
现在最后⼀层判断就是选取的这些数字和必须等于M ,即根据N ⽣成的对应⼆进制所在元素上的和是否为M
⽐如1110 ,我们应该判断arr[0] + arr[1] + arr[2] 是否为M 。
那么问题也就转化成了如何判断数组下标是否在1110 中呢?如何在则求和
其实也很简单,⽐如下标1 在,⽽下标3 不在。我们把1 转化成0100 ,1110 & 0100 为0100 ,⼤于0 ,因此下标1 在。⽽1110 & 0001 为0 ,因此下标3 不在。
所以求和我们可以如下实现:
ini 代码解读复制代码let arr = [1,2,3,4]
// i 为满⾜条件的⼆进制
let i = 0b1110
let s = 0, temp = []
let len = arr.length
for (let j = 0; j < len; j++) {
if ( i & 1 << (len - 1 - j)) {
s += arr[j]
temp.push(arr[j])
}
}
console.log(temp)
// => [1, 2, 3]
最终实现
scss 代码解读复制代码// 参数依次为⽬标数组、选取元素数⽬、⽬标和
const search = (arr, count, sum) => {
// 计算某选择情况下有⼏个 1,也就是选择元素的个数
const getCount = num => {
let count = 0
while(num) {
num &= (num - 1)
count++
}
return count
}
let len = arr.length, bit = 1 << len, res = []
// 遍历所有的选择情况
for(let i = 1; i < bit; i++){
// 满⾜选择的元素个数 === count
if(getCount(i) === count){
let s = 0, temp = []
// 每⼀种满⾜个数为 N 的选择情况下,继续判断是否满⾜ 和为 M
for(let j = 0; j < len; j++){
// 建⽴映射,找出选择位上的元素
if(i & 1 << (len - 1 -j)) {
s += arr[j]
temp.push(arr[j])
}
}
// 如果这种选择情况满⾜和为 M
if(s === sum) {
res.push(temp)
}
}
}
return res
}
1.3 编写⼀个程序,找到两个单链表相交的起始节点
编写⼀个程序,找到两个单链表相交的起始节点。
如下⾯的两个链表:
在节点c1开始相交。
⽰例1:
ini 代码解读复制代码输⼊:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, sk
输出:Reference of the node with value = 8
输⼊解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各⾃的表头开始算起,链表
⽰例2:
ini 代码解读复制代码输⼊:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB =
输出:Reference of the node with value = 2
输⼊解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各⾃的表头开始算起,链表
⽰例3:
css 代码解读复制代码输⼊:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输⼊解释:从各⾃的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,
解释:这两个链表不相交,因此返回 null。
注意:
• 如果两个链表没有交点,返回null.
• 在返回结果后,两个链表仍须保持原有的结构。
• 可假定整个链表结构中没有循环。
• 程序尽量满⾜O(n)时间复杂度,且仅⽤O(1)内存。
⽅法:遍历
• 先将两个链表转换成数组结构
• 对其中⼀个数组进⾏遍历,每次遍历判断另⼀个数组是否含有该值
• 遍历结束后如果没有找到就返回null
scss 代码解读复制代码function intersectNode( head1, head2 ) {
var arr1 = [],
arr2 = [],
theValue = null;
transformToArray( head1, arr1 );
transformToArray( head2, arr2 );
for (var i = 0; i < arr1.length; i++) {
if( arr2.indexOf(arr1[i]) !== -1 ){
theValue = arr1[i];
return 'Reference of the node with value = ' + theValue;
}
}
return theValue;
/*
arr1.some( value => {
if ( arr2.includes(value) ){
theValue = value;
return true;
}
})
if( theValue ){
return 'Reference of the node with value = ' + theValue;
}
return null;
*/
}
function transformToArray( head, arr ) {
while( head ){
arr.push(head.val);
head = head.next;
}
}
测试代码:
scss 代码解读复制代码function CreateNode(val){
this.val = val;
this.next = null;
}
function CreateList(...nodes){
this.head = nodes[0];
this.length = nodes.length;
for( var i = 0; i < nodes.length - 1; i++ ){
if( nodes[i+1] ){
nodes[i].next = nodes[i+1];
}
}
}
function intersectNode( head1, head2 ) {
var arr1 = [],
arr2 = [],
theValue = null;
transformToArray( head1, arr1 );
transformToArray( head2, arr2 );
for (var i = 0; i < arr1.length; i++) {
if( arr2.indexOf(arr1[i]) !== -1 ){
theValue = arr1[i];
return 'Reference of the node with value = ' + theValue;
}
}
return theValue;
}
function transformToArray( head, arr ) {
while( head ){
arr.push(head.val);
head = head.next;
}
}
const node1 = new CreateNode(1);
const node2 = new CreateNode(2);
const node3 = new CreateNode(3);
const node4 = new CreateNode(4);
const node5 = new CreateNode(5);
const nodeA = new CreateNode('A');
const nodeB = new CreateNode('B');
const nodeC = new CreateNode('C');
const nodeD = new CreateNode('D');
const nodeE = new CreateNode('E');
const list1 = new CreateList(node1, node2, node3, node4, node5);
const list2 = new CreateList(nodeA, nodeB, node3, nodeC, nodeD); intersectNode(list1.head,
list2.head);
1.4 翻转字符串⾥的单词
给定⼀个字符串,逐个翻转字符串中的每个单词。
⽰例1:
arduino 代码解读复制代码输⼊: "the sky is blue"
输出: "blue is sky the"
⽰例2:
makefile 代码解读复制代码输⼊: " hello world! "
输出: "world! hello"
解释: 输⼊字符串可以在前⾯或者后⾯包含多余的空格,但是反转后的字符不能包括。
⽰例3:
makefile 代码解读复制代码输⼊: "a good example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含⼀个。
说明:
• ⽆空格字符构成⼀个单词。
• 输⼊字符串可以在前⾯或者后⾯包含多余的空格,但是反转后的字符不能包括。
• 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含⼀个。
解法⼀:正则+JSAPI
javascript 代码解读复制代码var reverseWords = function(s) {
return s.trim().replace(/\s+/g, ' ').split(' ').reverse().join(' ')
};
解法⼆:双端队列(不使⽤API)
双端队列,故名思义就是两端都可以进队的队列
解题思路:
• ⾸先去除字符串左右空格
• 逐个读取字符串中的每个单词,依次放⼊双端队列的对头
• 再将队列转换成字符串输出(以空格为分隔符)
画图理解:
代码实现:
sql 代码解读复制代码var reverseWords = function(s) {
let left = 0
let right = s.length - 1
let queue = []
let word = ''
while (s.charAt(left) === ' ') left ++
while (s.charAt(right) === ' ') right --
while (left <= right) {
let char = s.charAt(left)
if (char === ' ' && word) {
queue.unshift(word)
word = ''
} else if (char !== ' '){
word += char
}
left++
}
queue.unshift(word)
return queue.join(' ')
};
.....................
需要完整版的可以看置顶文章评论区
评论记录:
回复评论: