某为机试题复盘以及面试体验

javascript 2021-09-06 215 次浏览 次点赞

前言

近期,在招聘网站投了某为的某前端岗,电话面试了一些技术问题之后,有幸去了某为基地进行机试,终于抽出时间进行复盘。

复盘的目的有二。一是加深自己的对机试题的印象,查缺补漏,提升自己的技术水平。二是放在此处,待有缘人进行参考,觉得有帮助可以来个赞。

招聘流程

笔者是在某招聘网站看到招聘信息,前端外包岗。

简单概况流程:投递简历->电话面试->机试->hr面

电话面试

简历是直接投递到项目主管那里去的(不是投给外包公司的哟),之后接到主管的电话,因为投递的是前端岗,自然聊了很多前端技术问题。

大致聊了flex布局、闭包、箭头函数、原型链、call与apply的区别等绕不开的点,之后聊了vue框架的双向绑定原理(vue层面和原生层面)、响应式原理。

感觉都是些很基础偏底层,有些东西项目中可能压根用不到,但是玩前端又必须掌握的知识。

电话面试完,该主管就约我到基地进行机试(说是重点要看写代码速度,业务型项目),并交换了联系方式(中间有些过程略过不表)。

当然第一次约的时间黄了,然后又改约到几天之后。(前后大概等待了四五天的样子)

机试

时间来到面试当天,手机收到访客验证码(这里必须说下感受,一下子就体验到大公司的高大上了)。

到达基地后,本以为是直接拿着验证码跟保安大哥说就能进了,但是问了之后才知道,要先去一个外面的接待厅(门外的一个一层的建筑,不太大的样子)。

进入接待厅之后,提供访客验证码,工作人员会帮忙打印一个访客证。拿到访客证之后就能进入大门了,当然问了一下保安大哥路咋走(题外话,还没进门在找园区的就感觉到大公司的实力雄厚)。

到达面试所在一楼大厅之后,请注意,又个小细节要来了,一楼大厅工作人员直接告知需要访客卡上面的人下来接待,随后让我在等待区坐着等候,我随即联系了面试官(项目主管)

之后面试官带我到达了所在楼层,掏出一部电脑就带领来到一个角落(类似办公区侧边的小休息室),然后机试也随之开始。

机试题一 二分法查找

来到电脑面前,用的是sublime text编辑器(此处补充一个小细节,编辑器只是工具,建议市面上流行的编辑器都要使用一段时间,不然机试的时候连编辑器都不会用尴尬了,因为我自己写前端vscode、sublime text、webstrom都用过)。

因为面试官跟我一直强调是看写代码的速度,说不是考算法,所以我头也比较铁,上来就是一个for循环实现,不到两分钟就搞完了。

/**
  * nums array 数组
  * target number 要查找的数字
  * return index
  */
let search = function(nums,target){
  let len = nums.length;
  for(let i = 0;i < len;i++){
    if(nums[i] === target) return i;
  }
}

很明显,我这个答案是不符合题目要求,因为面试官强调了一遍要用二分法来实现。

可是偶不会二分法(小声bb,我发誓我写过的项目都比较low,里面都没有用过二分法,虽然以前手写过但是太久没用都忘记了),那咋办呢?

菜就是菜,那就承认呗。我问面试官我能否查阅资料(试图挣扎,看看能不能搞出来),面试官思考了片刻,随手就整上机试题二(从这里也能看出,华为的大神还是挺多的,算法可能是只是基操)。

机试题二等到后面讨论,这里先来复盘一下用二分法怎么实现查找。

二分法简介

二分法,又名折半查找,是一种分而治之思想在排序算法上的典型应用。

概念神马的就不去搬了,再说说应用场景。

二分法的适用情况一般满足以下几点:

  1. 该数组数据量巨大,需要对处理的时间复杂度进行优化;
  2. 该数组已经排序;
  3. 一般要求找到的是某一个值或一个位置。

面试官可能想考察的点

那么问题来了,面试官的这个题目究竟是想考察什么?

经过分析,我得出两点可能:

1、考算法。其实也就是考思维能力和代码应用。
2、考查递归。因为递归也是面试热点和难点。

说好的考代码速度呢,说好的不考算法呢,人与人之间最基本的信任呢?

哈哈哈哈,其实换个角度来思考,也许是因为大神们的技术水平高,觉得这种简单的排序算法压根就不能称之为算法呢~手动滑稽保命

二分法查找 递归实现

那么假如他日又遇二分法该怎么应对呢?

一、不管给定数组是否排序,先来上一波排序,因为很多已经掌握的技术就是细节容易被挂

// ES6箭头函数写法 委婉的表明自己熟悉es6语法 又是一个小细节
arr.sort((a,b) => a - b);

二、二分查找的实现,重点是理解分而治之的思想以及递归的使用(推荐使用递归,因为理解使用递归之后,再去写不使用递归的会更加容易上手)

function binary(arr,target,leftIndex,rightIndex){

  // 递归出口 说明没有找到
  if(leftIndex > rightIndex) return -1;

  // 查找基准值索引
  let centerIndex = Math.floor((leftIndex + rightIndex) / 2);

  // 继续二分查找
  // 如果要查找的值 小于 基准值 说明要继续往左边这一半进行查找
  if(arr[centerIndex] > target){
    return binary(arr,target,leftIndex,centerIndex - 1);
  }else if(arr[centerIndex] < target){
    // 如果要查找的值 大于 基准值 说明要继续往右边这一半进行查找
    return binary(arr,target,centerIndex + 1,rightIndex);
  }else{
    // 嗯 查找到了就返回对应索引
    return centerIndex;
  }
}

重新回顾加手写完之后,感觉这个二分法真的不要太简单,所以说到底面试还是要注意自己平时忽略的细节呀。

当然写完之后,还是要礼貌性的测试一下。

二分法查找 非递归实现

就像我上面说到的,有可能面试官也会要求不能使用递归,那么这个时候我们应该怎么实现呢?

其实只要理解了递归实现二分查找的思路之后,应该是很容易进行改写的。

function binary(arr,target){
  // 初始化开始索引
  let leftIndex = 0;
  // 初始化结束索引
  let rightIndex = arr.length - 1;
  
  // whlie循环二分查找 本身也就类似递归折半再折半
  while(leftIndex <= rightIndex){
    // 索引未越界或者相等时进行循环 类似与递归出口

    // 基准值(中间)索引 方便折半
    let centerIndex = Math.floor((leftIndex + rightIndex) / 2); 
    
    // 继续二分查找 主要通过改变开始索引和结束索引来实现折半
    if(arr[centerIndex] > target){
      // 如果基准值 小于 目标值 则继续往左边一半查找
      rightIndex = centerIndex - 1;
    }else if(arr[centerIndex] < target){
      // 如果基准值 大于 目标值 则继续往右边一半进行查找
       leftInex = centerIndex + 1;
     }else{
       // 不大于不等于 说明查找到了 返回索引
       return centerIndex;
     }
  }

  // 没有找到 返回-1
  return -1;
}

是的,手写完之后感觉理解更加深刻了。觉得其实真的不难,非常的so easy。主要的难点可能是确定每次查找的边界索引。

所以这个二分法查找题,确实难度不大,当然,没接触过估计就头大了。

机试题二 数组去重

万万没想到,我居然会被一个数组去重题给抓瞎了,自认为这个题目确实也非常的简单,但是为什么当时就抓瞎了呢?

我总结原因如下:
一、受第一个题失败影响,导致思路开始有点跑偏。
二、平时确实比较少关注到这个点。
三、就是菜到抠脚。

当时的思路有两个,
第一,想通过for循环实现,但是发现必须要先遍历数组所有项之后再来去重,抛开效率不谈,这种写法注定需要花点时间,那么写代码的速度就没办法保证了。
第二,想通过fliter方法实现,但是当时卡壳了,不知道该怎么着手(因为数组的重复项可能不会相邻,并且重复项可能多个,例如[1,3,5,3,8,1,5])。

当然,最后面试官跟我说可以通过object.keys实现。

所以既然是复盘,那么三个思路都必须去搞咯。

数组去重思路一 for循环实现

就像我上面说到的一样,遍历数组,只是我跑偏了,我没有想到使用indexOf来查找重复项,而是妄想通过逐项对比来找出重复项(可想而知这个想法会造成多大的工作量以及复杂度)。

function arrDedup(arr){
  // 查找数组重复项
  let len = arr.length;
  // 初始化空数组 用来存放重复项
  let dedup_arr = [];
  for(let i = 0;i < len;i++){
    // 如果没有找到当前项 说明没有重复
    if(arr.indexOf(arr[i]) === -1) dedup_arr.push(arr[i]);
  }
}

思路清晰之后,写起来真的是快得飞起,唉,菜是原罪。

数组去重思路二 fliter方法实现

按照我当时的情况来说,其实使用fliter方法是最优选,然而当时又卡壳了。

卡壳的原因也许就是fliter方法去重的要点,所以我们需要先来看看fliter方法。

数组的filter方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

语法和参数:array.filter(function(currentValue,index,arr), thisValue)

如果按照for循环的思路借助indexOf来写,我们可以这样

let newArr = arr.filter((currentValue,index,arr) => arr.indexOf(currentValue) === index,this);

当然,现在模拟一个情况,假如下次的时候,我又卡壳indexOf了呢?

不要方,先对数组进行排序,然后去除相邻的项。(这个方法也可以延伸到前面的for循环去实现)

// 对数组进行排序
arr.sort((a,b) => a - b);
// 使用fliter去除相邻的相同项
let newArr = arr.filter((currentValue,index,arr) => currentValue !== arr[index + 1],this);

当然,假如上面的这个方法遇到英文字符串['a','d','b','f','e']就要歇菜了。

这个东西等以后有机会再来补充吧。

回到正题,数组去重其实写完发现并不难,主要是indexOf的点能否想到,如果没有想到,又要如何去保证去除多个重复的项呢-->排序。

数组去重思路三 object.keys方法实现

function arrDedup(arr){ {
    let obj = {};
    // 先使用map方法将每项都作为对象的key
    arr.map((item) => {
        obj[item] = 1;
    })
    // 如果属性名的类型是Number,那么Object.keys返回值是按照key从小到大排序
    return Object.keys(obj);
}

这个思路真的让我耳目一新,我确实没接触过用Object.keys排序。

当然,使用的场景需要注意。详情可以参考5分钟彻底理解Object.keys

机试题三 数组对象排序

// 根据obj.size对objs数组中的对象进行排序
function obj_sort(objs){

}

题目就是这样的,我当时第一反应当然是通过sort来解决,但是到写的时候我又走入误区了。

卡壳代码

// 错误写法
obj.size.sort((a,b) => a - b);

为了更好理解这个题目,我们先来假设一组数据吧。

// 假设数据
let objs = [
{id: 3, "size": 10},
{id: 6, "size": 5},
{id: 7, "size": 20},
{id: 9, "size": 1}
];

当假设数据一出来之后,是不是就很明了了。

然而当时我并没有去假设数据,为了追求速度,并且达到需求,发现sort卡壳所以我选择了手写冒泡排序来实现。

数组对象排序 冒泡排序实现

// 根据obj.size对objs数组中的对象进行排序
function obj_sort(objs){
  let len = objs.length;
  let temp;
  //for循环交换
  for(let i = 0;i < len - 1;i++){
   for(let j = 0;j < len - i -1;j++){
     // 如果前一项大于后一项直接交换
     if(objs[j].size > objs[j + 1].size){
       temp = objs[j];
       objs[j] = objs[j + 1];
       objs[j + 1] = temp;
     }
   }
  }
  return objs;
}

说实话,我对自己这个最low最笨的实现方式还算觉得可以吧(至少答上来了),用了不到三分钟左右就搞定了。

然而事实是,这也远远没发挥我该有的实力。

数组对象排序 sort方法实现

是的,这个题的最优解明显就是sort。

然而当时我为什么卡壳呢,因为我没有把数据假设出来,走入了误区,只顾盯着size了。

objs.sort((a,b) => a.size - b.size);

现在回过头复盘豁然开朗,然鹅当时真是菜哭了。

兴许是我的回答勉强让面试官觉得及格,所以叫来了hr,随后就进行了hr面。

hr面

hr来了之后,问了一些个人情况和工作背景之类的,还问了一些个人爱好之类的。

当然,也给了一些职业发展的建议,虽然这些建议我比她更深有体会。

之后就让回来等通知,说要对比一下其他候选人。

总结

总体面试体验只能说一般,但是这个是职场面试,本来大家都有明显目的指向,所以自然也不能要求人家把你当上帝对待,你又不是去旅游的游客,对吧。

技术面试和机试体验感比较印象深刻,因为无论是面试题还是机试题都能看出大企业对算法的重视,所以多刷leetcode对面大公司一定会有帮助的。当然,也能感觉到项目主管的相当的专业以及自己的菜,没有对比就没有伤害......

至于hr面的感受,略过不表......

我对自己本次差强人意的面试打个分,勉强厚着脸皮给自己个60及格吧!

当然,这次的发挥不好并不意味着以后也一样,所以只要不停下脚步,继续追逐继续打磨技术,会有一天手到拈来的。

写在最后

外包岗到底值不值得去?

我面试之前其实就了解了一下外包的模式以及看了些过来人给的经验。

我认为外包值不值得去要看三个点,

第一看公司,第二看项目,第三看人。

虽然看人放在了第三,但是我觉得这个比重是很大的。

我认为如果还有其他选择,请千万不要选外包,无论是多么高大上的外包!!!

大公司的外包面试可以用来积攒经验,无论以后去面大公司正式岗,或是去小公司都能给你很多启发。

素闻某些大公司的某些项目不把外包当人看,我自然一直是不信的。直到我去了这次面试,我有了新的体会。

最后想说,在我看来,大家都是为了生活来打工的,为什么还要来一出谁比谁更高贵呢?


本文由 大古 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论