珍爱网面经(一面)

越努力越幸运!

    其实自己最近的状态都不是很好,秋招已经快要结束了,也经过了8场面试了,目前只得到了一个小公司的offer,真的是劝退价,不能再少了。哎,继续复盘看书复习吧~坚持到10月底吧~

真的很多知识点看过,但是真的记不下来!

珍爱网的笔试题

涉及到自己不懂的知识点

一.jvm的参数

  -XmsSize(最小堆内存) 
 * -XmxSize(最大堆内存)
 * -XmnSize(分配给年轻代)
 * -XX:+PrintGCDetails 输出详细GC日志
 * -XX:SurvivorRatio=ratio Eden区和survivor区的比例
  -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
  -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
 * -XX:-UseTLAB 是否使用本地线程分配缓冲
 * -XX:+HeapDumpOnOutOfMemoryError(在出现异常的情况下  将内存快照dump出来) 
 * -XssSize 栈大小
 * -XX:MaxPermSize 方法区的最大值(JDK<=1.7 1.8将方法区移去,增加了metaspaces)
 * -XX:PermSize 方法区的大小

二.a=-2 表达式a>>1等于多少?

将一个数的二进制值向右>>或向左<<移位。执行左移时,在一个数的二进制形式中,

所有位都向左移动由移位运算符右侧的操作数指定的位数。 

移位后在右边留下的空位将由零来填充。右移位运算符的原理相似,只是朝相反的方向移位。

然而,如果数是负数,那么在左侧填充的值就是1而不是0。

两个移位 运算符是>>和<<,它们分别是右移位和左移位运算符。

a=-2

int 32位

2=**0000,0010, 取反为 ***1111,1101 ,在+1为:****1111,1110. 即-2的二进制

a>>1,右移动1位 111*******1,1111

是一个负数,变成正数 先-1为:1111******,1110, 在取反为00000*********,0001=1

所以结果-1.

a<<1,可以同样得出

-2=****1111,1110  左移动一位为 111111111,1100.

是一个负数,变成正数 先-1为:1111******,1011, 在取反为00000*********,0100=4

结果-4

所以 -2>>1=-1, -2<<1=-4

三.如何找到 java 程序 CPU 使用率100%的原因 

 1.用top 命令查看占用资源最多PID(进程):
     如 pid 为 1000;
   2.再用 top -H -p  1000 命令查看在这个进程中,消耗 cpu 最多 的线程,如 1003;
   3.  最后使用 jstack 1000 > dump_file 把这个进程的堆栈信息 dump 到文件中,
   4.打开 dump_file,找到 id 为1003的线程(要转化为16进制),就能发现是哪个方法占用了 cpu,分析自己的代码 

四.一个数组中只有一个数字是出现1次的,别的数字都是出现2次的,怎么找出只出现一次的数字呢? 

思路:数组为nums,则数组中所有数异或,最后的值为所求。

类似题目:

题目:数组中别的数都出现3次,只有一个数出现1次,找出该数。

思路:我们把数组中所有数字的二进制表示的每一位都加起来。如果某一位的和能被3整除,那么那个只出现一次的数字二进制表示中对应的那一位是0;否则就是1。

  int类型有32位,假如二进制位不进位的话,对数组中除了single数以外的所有数做累加,32位每一位上的值都是3。假如我对每一位是做模3加法的话,把数组所有数加起来,32位每一位的值应该有1有0,因为三个相同数加起来都会模3变为0,所以剩下的1都是属于single数的,所以这个累加和就等于single数。

    这个方法时间是O(32*N),但要分别计算每一位的累加,分别作32次累加。空间是O(1)。

 class Solution {
public:
    int singleNumber(int A[], int n) {
 
        int result = 0;
        int sum;
        int i, j;
        for(i=0;i<32;i++)
        {
            sum = 0;
            for(j=0;j<n;j++)
                sum += ((A[j]>>i)&1); //每轮只对固定某一位做累加
            
            if(sum%3 == 1)
                result = result|(1<<i);
        }
        return result;
    }
};

 题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请些程序找出这两个只出现一次的数字。要求时间复杂度为O(n),空间复杂度为O(1).

如输入数组{2,4,3,6,3,2,5,5},因为只有4,6这两个数字只出现一次,其他数字都出现了两次,所以输出4,6

    这是一个比较难的题目,很少有人在面试的时候不需要提示一下子想到最好的解决办法。一般当应聘者想了几分钟那个后还没有思路,面试官会给出一些提示。

     我们想到异或运算的一个性质:任何一个数字异或它自己都等于0,也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是哪个出现一次的数字,因为那些成对出现的两次的数字都在异或中低消了。

    我们试着把数组分成两个子数组,使得每个子数组只包含一次出现一次的数字,而其他数字都成对出现两次。如果能够这样拆分成两个数组,我们就可以按照前面的办法分别找出两个只出现一次的数字了。

我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或的结果。因为其他数字都出现两次,在异或中全部抵消了。由于这两个数字肯定不一样,那么异或的结果肯定不为0,也就是说在这个结果数字的二进制表示中至少有一位为1.我们在结果数字中找到最右边为1的位的位置,记为第n位。现在我们以第n位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中的每个数字的第n位都是1,而第二个子数组中每个数字的第n位都为0.由于我们分组的标准是数字中的某一位是1还是0,那么出现了两次的数字肯定被分配到同一个子数组中。因为两个相同的数字的任意一位都是相同的,我们不可能把两个相同的数字分配到两个子数组中去,于是我们已经把原数组分成了两个子数组,每个子数组都包含了一个只出现一次的数字,而其他数字都出现了两次。我们已经知道如何在数组中找出唯一一个只出现一次的数字,因此到此为止所有的问题都解决了。
 public void findNumsAppearOnce(int[] arr){  
        if(arr == null)  
            return;  
        int number = 0;  
        for(int i: arr)  
            number^=i;  
        int index = findFirstBitIs1(number);  
        int number1= 0,number2 = 0;  
        for(int i : arr){  
            if(isBit1(i,index))  
                number1^=i;  
            else  
                number2^=i;  
        }  
        System.out.println(number1);  
        System.out.println(number2);  
    }  
    private int findFirstBitIs1(int number){  
        int indexBit = 0;  
        while((number & 1)== 0){  
            number = number >> 1;  
            ++indexBit;  
        }  
        return indexBit;  
    }  
    private boolean isBit1(int number,int index){  
        number = number >>index;  
        return (number & 1) == 0;  
    }  

--------------------- 
作者:水的化合物的专栏 
来源:CSDN 
原文:https://blog.csdn.net/abc7845129630/article/details/52745029?utm_source=copy 
版权声明:本文为博主原创文章,转载请附上博文链接!

五.逻辑题

如果天平最多一次只可以称8个小球,(只能显示8个小球的重量大小,不能显示每个小球的具体数量),如果64个重量各不相同的小球,那么至少经过多少次称量才可以得到最重的4个小球呢?

答案是11次,但是我没有想出具体的分析过程。


珍爱网一面(注:笔试结束一定要回来查自己不懂的题目,面试会问的,前端朋友直接就当场一题一题分析试卷,听说只要卷子写满,笔试就可以通过

真的讲的乱七八糟的今天,语言描述的很不好,其实很多都是自己懂的。

1.自我介绍

2.mysql优化

1.SQL语句优化

1)应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
2)应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
3)很多时候用 exists 代替 in 是一个好的选择
4)用Where子句替换HAVING 子句 因为HAVING 只会在检索出所有记录之后才对结果集进行过滤

2.索引优化

索引优化策略

  • 最左前缀匹配原则,上面讲到了
  • 主键外检一定要建索引
  • 对 where,on,group by,order by 中出现的列使用索引
  • 尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0
  • 对较小的数据列使用索引,这样会使索引文件更小,同时内存中也可以装载更多的索引键
  • 索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
  • 为较长的字符串使用前缀索引
  • 尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可
  • 不要过多创建索引, 权衡索引个数与DML之间关系,DML也就是插入、删除数据操作。这里需要权衡一个问题,建立索引的目的是为了提高查询效率的,但建立的索引过多,会影响插入、删除数据的速度,因为我们修改的表数据,索引也需要进行调整重建
  • 对于like查询,”%”不要放在前面。 
    SELECT * FROMhoudunwangWHEREunameLIKE'后盾%' -- 走索引 
    SELECT * FROMhoudunwangWHEREunameLIKE "%后盾%" -- 不走索引
  • 查询where条件数据类型不匹配也无法使用索引 
    字符串与数字比较不使用索引; 

3.数据库结构优化

1)范式优化: 比如消除冗余(节省空间。。) 2)反范式优化:比如适当加冗余等(减少join) 3)拆分表: 分区将数据在物理上分隔开,不同分区的数据可以制定保存在处于不同磁盘上的数据文件里。这样,当对这个表进行查询时,只需要在表分区中进行扫描,而不必进行全表扫描,明显缩短了查询时间,另外处于不同磁盘的分区也将对这个表的数据传输分散在不同的磁盘I/O,一个精心设置的分区可以将数据传输对磁盘I/O竞争均匀地分散开。对数据量大的时时表可采取此方法。可按月自动建表分区。
4)拆分其实又分垂直拆分和水平拆分: 案例: 简单购物系统暂设涉及如下表: 1.产品表(数据量10w,稳定) 2.订单表(数据量200w,且有增长趋势) 3.用户表 (数据量100w,且有增长趋势) 以mysql为例讲述下水平拆分和垂直拆分,mysql能容忍的数量级在百万静态数据可以到千万 垂直拆分:解决问题:表与表之间的io竞争 不解决问题:单表中数据量增长出现的压力 方案: 把产品表和用户表放到一个server上 订单表单独放到一个server上 水平拆分: 解决问题:单表中数据量增长出现的压力 不解决问题:表与表之间的io争夺
方案: 用户表通过性别拆分为男用户表和女用户表 订单表通过已完成和完成中拆分为已完成订单和未完成订单 产品表 未完成订单放一个server上 已完成订单表盒男用户表放一个server上 女用户表放一个server上(女的爱购物 哈哈)

数据库的分表分库

4.服务器硬件优化

3.mysql索引什么时候不生效?

1.like语句 
2.列类型为字符串类型,查询时没有用单引号引起来 
3.在where查询语句中使用表达式 
4.在where查询语句中对字段进行NULL值判断 
5.在where查询中使用了or关键字, myisam表能用到索引, innodb不行;(用UNION替换OR,可以使用索引) 
6.全表扫描快于索引扫描(数据量小时)

4.笔试结束有没有查过不懂得题,问了逻辑题的实现(可能是因为我的逻辑题错误的原因)

5.ArrayList的底层实现(回答的时候,措辞出现了错误,使用了数组可以扩容的说法,这是不对的)

ArrayList:它是实现了List接口的,它的底层实现是数组的,它通常会设置一个初始容量,默认是10,如果在要加入一个元素的时候,大于这个初始容量的话,就会进行扩容操作,会编程初始容量的1.5倍

6.会先问一下平常使用的jdk版本,hashmap的底层实现,描述一下get过程。

7.有没有使用过线程池,是自己写一个线程池还是直接使用JDK提供的线程池,描述一下这几个线程池的特点和应用场景

 
使用线程池需要注意的地方
① 合理设置线程池的核心数量大小;
② 不要对那些同步等待其他任务结果的任务排队,以免引起死锁;
③ 在为时间可能很长的操作使用合用的线程时要小心,避免阻塞其他线程。
 
newCachedThreadPool:
 
底层:返回ThreadPoolExecutor实例,corePoolSize为0;maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为60L;unit为TimeUnit.SECONDS;workQueue为SynchronousQueue(同步队列)
通俗:当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁。
适用:执行很多短期异步的小程序或者负载较轻的服务器
newFixedThreadPool:
 
底层:返回ThreadPoolExecutor实例,接收参数为所设定线程数量nThread,corePoolSize为nThread,maximumPoolSize为nThread;keepAliveTime为0L(不限时);unit为:TimeUnit.MILLISECONDS;WorkQueue为:new LinkedBlockingQueue<Runnable>() 无解阻塞队列
通俗:创建可容纳固定数量线程的池子,每隔线程的存活时间是无限的,当池子满了就不在添加线程了;如果池中的所有线程均在繁忙状态,对于新任务会进入阻塞队列中(无界的阻塞队列)
适用:执行长期的任务,性能好很多
newSingleThreadExecutor:
 
底层:FinalizableDelegatedExecutorService包装的ThreadPoolExecutor实例,corePoolSize为1;maximumPoolSize为1;keepAliveTime为0L;unit为:TimeUnit.MILLISECONDS;workQueue为:new LinkedBlockingQueue<Runnable>() 无解阻塞队列
通俗:创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)
适用:一个任务一个任务执行的场景
NewScheduledThreadPool:
 
底层:创建ScheduledThreadPoolExecutor实例,corePoolSize为传递来的参数,maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为0;unit为:TimeUnit.NANOSECONDS;workQueue为:new DelayedWorkQueue() 一个按超时时间升序排序的队列
通俗:创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构
适用:周期性执行任务的场景
线程池任务执行流程:
 
当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
备注:
 
一般如果线程池任务队列采用LinkedBlockingQueue队列的话,那么不会拒绝任何任务(因为队列大小没有限制),这种情况下,ThreadPoolExecutor最多仅会按照最小线程数来创建线程,也就是说线程池大小被忽略了。
 
如果线程池任务队列采用ArrayBlockingQueue队列的话,那么ThreadPoolExecutor将会采取一个非常负责的算法,比如假定线程池的最小线程数为4,最大为8所用的ArrayBlockingQueue最大为10。随着任务到达并被放到队列中,线程池中最多运行4个线程(即最小线程数)。即使队列完全填满,也就是说有10个处于等待状态的任务,ThreadPoolExecutor也只会利用4个线程。如果队列已满,而又有新任务进来,此时才会启动一个新线程,这里不会因为队列已满而拒接该任务,相反会启动一个新线程。新线程会运行队列中的第一个任务,为新来的任务腾出空间。
 
这个算法背后的理念是:该池大部分时间仅使用核心线程(4个),即使有适量的任务在队列中等待运行。这时线程池就可以用作节流阀。如果挤压的请求变得非常多,这时该池就会尝试运行更多的线程来清理;这时第二个节流阀—最大线程数就起作用了。

 

8.算法题:有序数组怎么变成无序数组,分析它的时间复杂度

 

9.场景题:珍爱网现有100万menberid,不重复的,假设最多有一亿个会员,现在怎么判断新增的一个memberid是否已经存在了?

10.自己的学习路径

11.优缺点

 

猜你喜欢

转载自blog.csdn.net/hezuo1181/article/details/83043059