初中级面试题(算法和Nodejs)

788 阅读6分钟

其实算法方面在前端的实际项目中涉及得并不多,但还是需要精通一些基础性的算法,一些公司还是会有这方面的需求和考核,建议大家还是需要稍微准备下,这属于加分题。

  • 栈,后进后出,push,pop
  • 队列,先进先出,push,shift
  • 链表,结点,对元素的内存地址一无所知(每一个结点的结构都包括了两部分的内容,数据域和指针域

五大算法

  • 贪心算法: 局部最优解法
  • 分治算法: 分成多个小模块,与原问题性质相同
  • 动态规划: 每个状态都是过去历史的一个总结
  • 回溯法: 发现原先选择不优时,退回重新选择
  • 分支限界法

基础排序算法

// 冒泡排序: 两两比较
function bubleSort(arr) {
        var len = arr.length;
        for (let outer = len ; outer >= 2; outer--) {
            for(let inner = 0; inner <=outer - 1; inner++) {
                if(arr[inner] > arr[inner + 1]) {
                    [arr[inner],arr[inner+1]] = [arr[inner+1],arr[inner]]
                }
            }
        }
        return arr;
    }
​
// 选择排序: 遍历自身以后的元素,最小的元素跟自己调换位置
function selectSort(arr) {
    var len = arr.length;
    for(let i = 0 ;i < len - 1; i++) {
        for(let j = i ; j<len; j++) {
            if(arr[j] < arr[i]) {
                [arr[i],arr[j]] = [arr[j],arr[i]];
            }
        }
    }
    return arr
}
​
// 插入排序: 即将元素插入到已排序好的数组中
function insertSort(arr) {
    for(let i = 1; i < arr.length; i++) {  //外循环从1开始,默认arr[0]是有序段
        for(let j = i; j > 0; j--) {  //j = i,将arr[j]依次插入有序段中
            if(arr[j] < arr[j-1]) {
                [arr[j],arr[j-1]] = [arr[j-1],arr[j]];
            } else {
                break;
            }
        }
    }
    return arr;
}

高级排序算法

  • 快速排序

    • 选择基准值(base),原数组长度减一(基准值),使用 splice
    • 循环原数组,小的放左边(left数组),大的放右边(right数组);
    • concat(left, base, right)
    • 递归继续排序 left 与 right
function quickSort(arr) {
    if(arr.length <= 1) {
        return arr;  //递归出口
    }
    var left = [],
        right = [],
        current = arr.splice(0,1); 
    for(let i = 0; i < arr.length; i++) {
        if(arr[i] < current) {
            left.push(arr[i])  //放在左边
        } else {
            right.push(arr[i]) //放在右边
        }
    }
    return quickSort(left).concat(current,quickSort(right));
}
  • 希尔排序:不定步数的插入排序,插入排序

  • 口诀: 插冒归基稳定,快选堆希不稳定

稳定性: 同大小情况下是否可能会被交换位置, 虚拟dom的diff,不稳定性会导致重新渲染;

递归运用(斐波那契数列): 爬楼梯问题

初始在第一级,到第一级有1种方法(s(1) = 1),到第二级也只有一种方法(s(2) = 1), 第三级(s(3) = s(1) + s(2))

function cStairs(n) {
    if(n === 1 || n === 2) {
        return 1;
    } else {
        return cStairs(n-1) + cStairs(n-2)
    }
}

斐波那契数列:

function f(n){
    if(n===0){ return 0}
    else if(n===1){return 1}
    else {
        let fn1 = 0,fn2=1,fnx=0;
        for(let i = 0;i<n-1;i++){
            let newFn = fn2;
            fnx=fn1+fn2;
            fn1=fn2;
            fn2=fnx;
        }
        return fnx
    }
}

递归算法:

function fib(count){
    let count = parseInt(count);
    if(isNaN(count) || count <=0){return 0}
    function fn(count){
        if(count <= 2){return 1}
        return fn(count-1)+fn(count-2)
    }
    return fn(count)
}

求前20个数字

let arr = [1,2];
for(let i = 3;i<20;i++){
    arr[i] = arr[i-1]+arr[i-2]
}
for(let i = 1;i<arr.length;i++){
    console.log(arr[i])
}

天平找次品

有n个硬币,其中1个为假币,假币重量较轻,你有一把天平,请问,至少需要称多少次能保证一定找到假币?

  • 三等分算法:

      1. 将硬币分成3组,随便取其中两组天平称量

      • 平衡,假币在未上称的一组,取其回到 1 继续循环
      • 不平衡,假币在天平上较轻的一组, 取其回到 1 继续循环

二叉树-递归函数(递归式,递归边界)

/ 先序遍历的遍历实现
// 所有遍历函数的入参都是根据结点对象
function preorder(root) {
 // 递归边界,root为空
 if(!root) {
  return
 }
 // 输出当前遍历的结点值
 console.log('当前遍历的结点值' + root.val);
 // 递归遍历左子树:
 preorder(root.left);
 // 递归遍历右子树:
 preorder(root.right);
}
​
// 中序遍历,左子树,根结点,右子树
function inorder(root) {
 if(!root) {
  return
 }
 // 递归遍历左子树
 inorder(root.left);
 // 输出当前遍历的结点值
 console.log('当前遍历的结点是', root.val);
 // 递归遍历右子树
 inorder(root.right);
}
​
// 后序遍历:左子树,右子树,根结点
function postorder(root) {
 if(!root) {
  return
 }
 // 递归遍历左子树
 postorder(root.left)
 // 递归遍历右子树
 postorder(root.right)
 // 输出当前遍历的结点值
 console.log('当前遍历的结点值是:'+root.val)
}

Nodejs

什么是nodejs

Nodejs是服务器端的一门技术,它是基于Google V8 JavaScript引擎而开发的,用来开发可扩展的服务端程序。

  • 为什么要用nodejs

    Nodejs会让我们的编程工作变得简单,它主要包含以下几个好处:

    执行快速,永远不会阻滞,独立的异步处理机制,避免了并行带来的问题。而JavaScript又是通用的编程语言。

  • Nodejs有哪些特点

    Nodejs是单线程的,但是有很高的可扩展性,使用JavaScript作为主流编程语言,使用的是异步处理机制和事件驱动,处理高效。

  • 为什么nodejs是单线程的

    Nodejs使用的是单线程,通过异步处理的方式,可以处理大量的数据吞吐量,从而有更好的性能和可扩展性。

  • 什么是回调函数

    回调函数是指一个函数作为参数传入另一个函数,这个函数会被在某个时机调用。

  • 什么是回调地狱

    回调地狱是由嵌套的回调函数导致的,这样的机制会导致有些函数无法到达,并且很难维护。

  • 如何阻止回调地狱

    有三种方法,对每个错误都要处理到,保证代码的贯通,程序代码模块化。

Node 的 Event Loop: 6个阶段

  • timer 阶段: 执行到期的setTimeout / setInterval队列回调

  • I/O 阶段: 执行上轮循环残流的callback

  • idle, prepare

  • poll: 等待回调

      1. 执行回调
      1. 执行定时器

      • 如有到期的setTimeout / setInterval, 则返回 timer 阶段
      • 如有setImmediate,则前往 check 阶段
  • check

    • 执行setImmediate
  • close callbacks

Nodejs和ajax的区别是什么

Nodejs和ajax都是通过JavaScript来表现的,但是它们的目的是不同的。Ajax是设计用来动态的更新页面的某个区域,从而不需要更新整个页面。Nodejs是用来开发客户服务器类型应用的。

Nodejs中的chaining是什么

Chaining是指从一个数据流到另一个数据流的链接,从而实现多个流操作。

什么是streams,有哪些类型

流的概念是不间断的,它可以不间断的从某个地方读取数据,或者从某个地方写入数据。流数据大致有4中类型:可读、可写、即可读又可写、转化。

退出代码是什么,有哪些退出代码

退出代码是指中断nodejs运行时返回的代码。退出代码有:

Unused Uncaught fatal exception fatal error non function internal exception handler

Internal exception handler run time failure internal JavaScript evaluation failure

什么是globals

有三个global的关键字。Global代表的是最上层的命名空间,用来管理所有其他的全局对象。Process是一个全局对象,可以把异步函数转化成异步回调,它可以在任何地方被访问,它主要是用来返回系统的应用信息和环境信息。Buffer是用来处理二进制数据的类。

Angular js和nodejs区别是什么

Angularjs是网络应用开发框架,而nodejs是一个实时系统。

Nodejs是如何支持多处理器平台的

Cluster模块是用来支持这方面的,它可以允许多个nodejs工作进程运行在相同的端口上。