面试笔记:面经-网易考拉


一、自我介绍

个人背景、项目经历、实习经历。


二、项目相关

2.1 React Native

React Native只使用JavaScript也能编写原生移动应用。 它在设计原理上和React一致,通过声明式的组件机制来搭建丰富多彩的用户界面。
React Native主要特性

  • 原生的iOS组件
    React Native主张“Learn once, write everywhere”而非其他跨平台工具一直宣扬的“Write once, run everywhere”。通过React Native,开发者可以使用UITabBar、UINavigationController等标准的iOS平台组件,让应用界面在其他平台上亦能保持始终如一的外观、风格。
  • 异步执行
    JavaScript应用代码和原生平台之间所有的操作都采用异步执行模式,原生模块使用额外线程,开发者可以解码主线程图像、后台保存至磁盘、无须顾忌UI等诸多因素直接度量文本设计布局。
  • 触摸处理
    React Native引入了一个类似于iOS上Responder Chain响应链事件处理机制的响应体系,并基于此为开发者提供了诸如TouchableHighlight等更高级的组件。

2.2 Wexx

Weex是使用流行的Web开发体验来开发高性能原生应用的框架。
集成了 WeexSDK 之后,可以使用JavaScript语言和前端开发经验来开发移动应用。

Weex渲染引擎与DSL语法层是分开的,Weex并不强依赖任何特定的前端框架。目前Vue.js和Rax这两个前端框架被广泛应用于Weex页面开发,同时Weex也对这两个前端框架提供了最完善的支持。Weex的另一个主要目标是跟进流行的Web开发技术并将其和原生开发的技术结合,实现开发效率和运行性能的高度统一。在开发阶段,一个Weex页面就像开发普通网页一样;在运行时,Weex页面又充分利用了各种操作系统的原生组件和能力。

  • Vue.js是一个不断进化中的前端框架。
  • Rax是提供类React语法和兼容性的前端框架。

三、Java后台

3.1 Spring中的IoC的理解

IoC即Inversion of Control,控制反转,是面向对象编程中的一种设计原则,可以用来降低计算机代码之间的耦合度。

  • 实现策略
    (1)依赖查找:Dependency Injection,简称DI。容器提供回调接口和上下文条件给组件。EJB和Apache Avalon都使用这种方式。组件就必须使用容器提供的API来查找资源和协作对象,仅有的控制反转只体现在那些回调方法上,容器将调用这些回调方法,从而让应用代码获得相关资源。
    (2)依赖注入:Dependency Lookup,简称DL。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。容器全权负责组件的装配,会把符合依赖关系的对象通过JavaBean属性或者构造函数传递给需要的对象。
    通过JavaBean属性注射依赖关系的做法称为设值方法注入(Setter Injection);
    将依赖关系作为构造函数参数传入的做法称为构造器注入(Constructor Injection)。
  • 实现方式
    (1)实现数据访问层。
    (2)模块与接口重构。
    (3)随时增加单元测试。
    (4)使用服务定位器而不是构造注入。

3.2 数据库MySQL

3.3 Hashmap底层实现及扩容

HashMap采用了数组链表的数据结构,在查询修改上继承了数组的线性查找和链表的寻址修改,它存储的内容是键值对(key-value)映射。

  • 数组中存放的是对象,数组部分进行的操作主要是散列。
    根据hash算法实现快速存储第一步,确定存储在数组的哪个位置。每一个单位中存储的内容为:key+value+指针,其中指针主要用来进行链表操作。
  • 链表存在主要是为了解决经过hash计算后会有重复值这一问题。
    当多次散列后,hash计算结果值一致,需要存放在数组的同一部分,就要使用链表结构。当一个非空数组中需要存入新的对象,就需要把原来对象中末尾的指针指向新的对象,每次有新的对象key值经过hash计算后得到相同的值,都会重复以上步骤,“往下塞”,使每个对象都能够被访问到。
  • HashMap扩容
    HashMap默认的负载因子大小为0.75,当一个map填满了75%数组的时候,和其它集合类一样,将会创建原来HashMap大小16的两倍的数组,来重新调整map的大小,并将原来的对象放入新的数组中。
  • HashMap元素的移动
    假设扩容前的map大小为2的N次方,扩容后map中的元素只有两种情况:
    元素hash值第N+1位为0:不需要进行位置调整。
    元素hash值第N+1位为1:调整至原位置+原数组容量位置。

3.4 一致性哈希算法

一致性哈希将每个对象映射到圆环边上的一个点,系统再将可用的节点机器映射到圆环的不同位置。查找某个对象对应的机器时,需要用一致性哈希算法计算得到对象对应圆环边上位置,沿着圆环边上查找直到遇到某个节点机器,这台机器即为对象应该保存的位置。 当删除一台节点机器时,这台机器上保存的所有对象都要移动到下一台机器。添加一台机器到圆环边上某个点时,这个点的下一台机器需要将这个节点前对应的对象移动到新机器上。 更改对象在节点机器上的分布可以通过调整节点机器的位置来实现。

  • 一致性哈希算法的实现原理其实很简单,其将整个哈希值空间组织成一个虚拟的圆环,比如某哈希函数H的值空间为0-232-1(即哈希值是一个32位无符号整形),那么整个哈希空间环看起来就像下图所示:
    在这里插入图片描述
  • 将机器映射到环中
    一致性哈希算法是数据分布的方式,而数据最终是需要存到机器中的,所以首先需要将机器映射到环中。假设有四台机器 Node1Node2Node3以及Node4,使用机器名或者ip计算这些机器的哈希值,然后分别映射到环中去,如下图:
    在这里插入图片描述
  • 将数据映射到环中
    使用相同的哈希函数计算需要存储数据的哈希值,假设现在有四份数据data1data2data3以及data4,需要将这些数据映射到环中去,如下图:
    在这里插入图片描述
    在这个哈希环中,数据存储的机器是按照顺时针方向遇到的第一个节点就是这个数据存储的节点,所以图中:
    数据data2存储在节点Node1上;
    数据data3存储在节点Node4上;
    数据data1存储在节点Node2上;
    数据data4存储在节点Node3上。
  • 添加节点
    现在往上面的哈希环添加一个节点Node5,同样使用相同的哈希函数计算这个节点在哈希环的位置,假设计算的结果如下:
    在这里插入图片描述
    添加节点Node5之后,原本存储在Node4上的数据data3就分配到Node5了,其他数据分配不变。相比于普通的哈希添加节点会导致大量映射失效,一致性哈希可以很好的处理这种情况。如果增加一台服务器,则受影响的数据仅仅是新服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它数据也不会受到影响。
  • 删除节点
    如果哈希环中有节点出现故障,需要从哈希空间中移除,影响的数据也是有限的。假设节点Node1出现故障需要移除,新的哈希空间的数据映射如下:
    在这里插入图片描述
    Node1节点移走之后,数据data2就存储到Node4节点上。所以如果从环中删除节点,受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。

四、算法题(口述)

4.1 二叉树两个节点的最大距离

计算一个二叉树的最大距离有两个情况:

  • 情况1:路径经过左子树的最深节点,通过根节点,再到右子树的最深节点。
    情况2:路径不穿过根节点,而是左子树或右子树的最大距离路径,取其大者。
  • 对于情况1来说,只需要知道左右子树的深度,然后加起来即可。
    对于情况2来说,需要知道左子树的最远距离,右子树的最远距离。
  • 只需要计算这两种情况的路径距离,并取其最大值,就是该二叉树的最大距离。
static int MaxLen = 0;

class Node {
    Node pLeft;
    Node pRight;
    int MaxLeft;
    int MaxRight;
    int data;

    public Node(int data) {
        this.data = data;
    }
}

public void FindMaxLen(Node pRoot) {
    if (pRoot == null) {
        return;
    }
    if (pRoot.pLeft == null) {
        pRoot.MaxLeft = 0;
    }
    if (pRoot.pRight == null) {
        pRoot.MaxRight = 0;
    }
    if (pRoot.pLeft != null) {
        FindMaxLen(pRoot.pLeft);
    }
    if (pRoot.pRight != null) {
        FindMaxLen(pRoot.pRight);
    }
    if (pRoot.pLeft != null) {
        int nTempMax = 0;
        nTempMax = pRoot.pLeft.MaxLeft > pRoot.pLeft.MaxRight ? pRoot.pLeft.MaxLeft : pRoot.pLeft.MaxRight;
        pRoot.MaxLeft = nTempMax + 1;
    }
    if (pRoot.pRight != null) {
        int nTempMax = 0;
        nTempMax = pRoot.pRight.MaxLeft > pRoot.pRight.MaxRight ? pRoot.pRight.MaxLeft : pRoot.pRight.MaxRight;
        pRoot.MaxRight = nTempMax + 1;
    }
    if (pRoot.MaxLeft + pRoot.MaxRight > MaxLen) {
        MaxLen = pRoot.MaxLeft + pRoot.MaxRight;
    }
}
发布了90 篇原创文章 · 获赞 10 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Fan0628/article/details/99762583