2019年_BATJ大厂面试题总结-华为篇

1.java的反射机制了解吗

答案:https://blog.csdn.net/qq_39209361/article/details/81239189

2.Java的数据类型,线程,内存

答案:https://blog.csdn.net/zj15527620802/article/details/80622314
https://www.cnblogs.com/skorzeny/p/6480012.html

3.rabbitMQ有哪些关键的组成部分?

publisher
publisher:发布消息
exchange
exchange:用来决定消息应该投递到哪些queue
queue
queue:用来存储并分发消息
consumer
consumer:消费消息
除此之外还有另外几个概念

routing_key
搭配binding_key用来过滤消息,这个属性会在消息发送时被决定
binding_key
用来绑定exchange和queue,和routing_key搭配在exchange决策哪些消息应该投递到哪些queue
exchange的类型:FANOUT,TOPIC,DIRECT
FANOUT:广播,此时binding_key和routing_key其实是无效的,消息将会投递到目标exchange绑定的所有queue上
DIRECT:消息将只会被投递到和routing_key一致的binding_key所绑定的queue上
TOPIC:和DIRECT相似,只是此时exchange的决策会考虑binding_key的通配符配置
详解:https://www.cnblogs.com/200911/p/4856835.html

4.STL中vector和map的区别,map底层如何删除一个元素

答案:
1、Vector是顺序容器,是一个动态数组,支持随机存取、插入、删除、查找等操作,在内存中是一块连续的空间。在原有空间不够情况下自动分配空间。vector随机存取效率高,但是在vector插入元素,需要移动的数目多,效率低下。
2、Map是关联容器,以键值对的形式进行存储,方便进行查找。关键词起到索引的作用,值则表示与索引相关联的数据。以红黑树的结构实现,插入删除等操作都在O(logn)时间内完成
总结:
1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
2、如果你需要大量的插入和删除,而不关心随即存取,则应使用list
3、如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。
删除:https://blog.csdn.net/wxm349810930/article/details/69663287

5.String,StringBuffer,聊了比较多的String相关的

源代码答案:https://www.cnblogs.com/kissazi2/p/3648671.html
详解:https://blog.csdn.net/shenhonglei1234/article/details/54908934

6.对Spring的理解,说说里面用到的设计模式

最详细的答案:https://blog.csdn.net/xunjiushi9717/article/details/94322213

7.多线程中生产者和消费者如何保证同步

答案:https://blog.csdn.net/aaron072/article/details/78611849

8.浏览器输入一个地址,数据在网络中是怎么传输的,每一层加了什么

答案:https://blog.csdn.net/swag_wg/article/details/102214865

9.你用的java哪个版本,java1.8有什么新特性

答案:https://blog.csdn.net/qq_29411737/article/details/80835658

10.你用过什么消息中间件?(RabbitMQ)

答案:RabbitMQ 入门到精通(这一篇就够了)

11.手撕代码:

[A,A,A,B,B,C]每次只能存一个元素,耗时为1时,相同元素需要隔n时才能存,问存的最小耗时。
思路:数据结构算法。

12.写代码:判断一棵树是不是平衡二叉树

解决思路一:按照前序遍历的路线判断。

1.判断以根结点的树是否为二叉平衡树。求出左右子树的高度,判断它们的高度差是否超过了1。
2.递归判断根的左子树是否为平衡二叉树
3.递归判断根的右子树是否为平衡二叉树
注意:空树也是平衡二叉树
代码实现:

//二叉树的高度(比较左右子树那个高,高的加1既为二叉树的高度)
int BinaryTreeHigh(BTNode* root)
{
    if (root == NULL)
    {
        return 0;
    }
    int ret1 = BinaryTreeHigh(root->_left);
    int ret2 = BinaryTreeHigh(root->_right);
    //二叉树的高度为左子树和右子树中高的那个高度加1
    return ret1 > ret2 ? ret1 + 1 : ret2 + 1;
}
//判断树是否为平衡二叉树(1:是 0:不是)
int IsBlancedTree_R(BTNode* root)
{
    //空树是平衡二叉树
    //平衡二叉树是指以当前结点为根的左右子树高度不得超过1
    if (root == NULL)
        return 1;
    //右子树深度
    int right = BinaryTreeHigh(root->_left);
    //左子树深度
    int left = BinaryTreeHigh(root->_right);
    int gap = right - left;
    //深度超过1不是
    if (gap > 1 || gap < -1)
        return 0;
    //递归判断子树
    return IsBlancedTree_R(root->_left) && IsBlancedTree_R(root->_right);
}

对于上边的代码,效率会很低,因为这种方法存在着许多重复的计算。以上图的例子分析,从12开始判断,用BinaryTreeHigh函数求深度时,要遍历35、9,而在判断以35位根的树是否为平衡二叉树时也要遍历9。因此,这种方法存在着许多重复的计算。那么我们怎么能让它们不重复呢?

解决思路二:按照后序遍历的路线判断

1.首先,判断它的左子树是否为平衡二叉树
2.然后在判断它的右子树是否为平衡二叉树
3.判断它们是否为平衡二叉树的同时,记录它们的左右子树的深度
4.最后在判断以这个结点为根的树是否为平衡二叉树
代码实现:

//判断树是否为平衡二叉树(1:是 0:不是)
//优化版本(不用遍历重复的结点)
int IsBlancedTree_op(BTNode* root, int *pdepth)
{
    if (root == NULL)
    {
        *pdepth = 0;
        return 1;
    }
    //按照后序遍历去判断,先判断左右子树,然后记录以当前结点为根树的深度
    int left, right;
    if (IsBlancedTree_op(root->_left, &left) && IsBlancedTree_op(root->_right, &right))
    {
        int gap = right - left;
        if (gap <= 1 && gap >= -1)
        {
            *pdepth = left>right ? left + 1 : right + 1;
            return 1;
        }
    }
    return 0;
}

13.自旋锁和互斥量的区别

互斥锁(mutexlock):
最常使用于线程同步的锁;标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁;临界区和互斥量都可用来实现此锁,通常情况下锁操作失败会将该线程睡眠等待锁释放时被唤醒。

在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。

  1. 原子性:把一个互斥量锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量;

  2. 唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;

  3. 非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。

自旋锁(spinlock):
同样用来标记只能有一个线程访问该对象,在同一线程多次加锁操作会造成死锁;使用硬件提供的swap指令或test_and_set指令实现;同互斥锁不同的是在锁操作需要等待的时候并不是睡眠等待唤醒,而是循环检测保持者已经释放了锁,互斥量阻塞后休眠让出cpu,而自旋锁阻塞后不会让出cpu,会一直忙等待,直到得到锁。这样做的好处是节省了线程从睡眠状态到唤醒之间内核会产生的消耗,在加锁时间短暂的环境下这点会提高很大效率。适用于锁的持有时间比较短。

发布了52 篇原创文章 · 获赞 86 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43107323/article/details/104701172