服务端开发之Java备战秋招面试3

今天继续学习,先做两题算法题练练手,在继续整理八股文,深入理解,才能在面试的时候有更好地表现,一起加油吧,希望秋招多拿几个令人心动的offer,冲吧。

目录

1、算法题:判断链表中是否有环

2、算法题:合并二叉树

3.什么是跳表?

4.Spring的特性?

5.mysql事务特性?mysql隔离级别?mysql怎么实现可重复读的呢?

6.讲一下B树,B+树,哈希表,AVL树,红黑树?

7.怎么保证HashMap的线程安全?

8. CAS(Compare And Swap) 、synchronized、volatile?

9.ThreadLocal是否可保证资源的同步?它会导致内存泄漏?

扫描二维码关注公众号,回复: 14739610 查看本文章

10.jvm 垃圾回收是什么时候触发?

11.线程池有哪几种类型?

12.TCP三次握手四次挥手?

13.OSI七层和TCP/IP四层?OSI五层?

14.算法题:两个有序链表合并?

15.算法题:反转链表


1、算法题:判断链表中是否有环

题目链接:判断链表中是否有环_牛客题霸_牛客网

判断链表是否有环,用快慢指针即可,快指针每次走两步,慢指针每次走一步,如果慢指针不为空,快慢指针能相遇,则链表有环,否则,链表无环。

注意:边界值的处理,防止空指针异常。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null || head.next == null){
            return false ;
        }
        ListNode fast = head;
        ListNode slow = head ;
        while(fast != null && fast.next != null){
            fast = fast.next.next ;
            slow = slow.next ;
            if(slow == fast){
                return true ;
            }
        }
        return false ;
    }
}

2、算法题:合并二叉树

可以使用递归法和迭代法合并,递归法如下,递归合并到树t1上。

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param t1 TreeNode类 
     * @param t2 TreeNode类 
     * @return TreeNode类
     */
    public TreeNode mergeTrees (TreeNode t1, TreeNode t2) {
        // write code here
        //合并到t1上
        if(t1 == null && t2 == null){
            return null ;
        }
        if(t1 == null){
            return t2 ;
        }
        if(t2 == null){
            return t1 ;
        }
        t1.val = t1.val + t2.val ;
        t1.left = mergeTrees(t1.left,t2.left) ;
        t1.right = mergeTrees(t1.right,t2.right) ;
        return t1 ;
    }
}

迭代法如下:

建立一个队列,层次遍历的方式,累加相应的节点值。

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param t1 TreeNode类 
     * @param t2 TreeNode类 
     * @return TreeNode类
     */
    public TreeNode mergeTrees (TreeNode t1, TreeNode t2) {
        // write code here
        //合并到t1上
      if(t1 == null && t2 == null){
        return null ;
      }
      if(t1 == null){
        return t2 ;
      }
      if(t2 == null){
        return t1 ;
      }
      Queue<TreeNode> queue = new LinkedList() ;
      queue.offer(t1) ;
      queue.offer(t2) ;
      while(!queue.isEmpty()){
        TreeNode node1 = queue.poll() ;
        TreeNode node2 = queue.poll() ;
        node1.val = node1.val + node2.val ;
        if(node1.left != null && node2.left != null){
            queue.offer(node1.left) ;
            queue.offer(node2.left) ;
        }
        if(node1.right != null && node2.right != null){
            queue.offer(node1.right) ;
            queue.offer(node2.right) ;
        }
        if(node1.left == null && node2.left != null){
            node1.left = node2.left ;
        }
        if(node1.right == null && node2.right != null){
            node1.right = node2.right ;
        }
      }
      return t1 ;
    }
}

3.什么是跳表?

1)跳表,又叫做跳跃表、跳跃列表,在有序链表的基础上增加了“跳跃”的功能。
2)跳表在原来的有序链表上加上了多级索引,通过索引来快速查找;可以支持快速的删除、插入和查找操作。
3)跳表实际上是一种增加了前向指针的链表,是一种随机化的数据结构。
4)Redis中 的 SortedSet、LevelDB 中的 MemTable 都用到了跳表。
5)对比平衡树, 跳表的实现和维护会更加简单, 跳表的搜索、删除、添加的平均时间复杂度是 O(logn)。
参考这篇博客:跳表(Skip List)_wink22的博客-CSDN博客_跳表

4.Spring的特性?

三大特性:依赖注入(DI),控制反转(IOC),面向切面编程(AOP).

可以参考这篇博客:【Spring篇】Spring的三大特性_我是不贪嘴吖的博客-CSDN博客_spring特性

5.mysql事务特性?mysql隔离级别?mysql怎么实现可重复读的呢?

ACID:原子性,一致性,隔离性,持久性

读未提交,读已提交,可重复读,串行化。

可重复读是Mysql的默认隔离级别,就是指在同一事务中多次读取的数据是一致的。

实现方法是 Mysql通过多版本并发控制(MVCC):我们常说的MVCC是由MySQL数据库InnoDB存储引擎实现的,并非是由MySQL本身实现的,不同的存储引擎,对MVCC都有不同的实现。就是在InnoDB引擎中,InnoDB在每行记录后面保存两个隐藏的列,分别保存了这个行的创建时间(版本号)和行的删除时间(版本号)。这里存储的并不是实际的时间值,而是系统版本号,当数据被修改时,版本号加1。在读取事务开始时,系统会给当前读事务一个版本号,然后,事务会读取版本号<=当前版本号的数据。此时如果其他写事务修改了这条数据(增删改),那么这条数据的版本号就会加1,从而比当前事务的版本号高,所以,当前事务读取的并不是更新后的数据,而是当前事务对应版本下的数据。
参考链接:mysql 可重复读的实现原理_Vance.的博客-CSDN博客_mysql可重复读实现原理

6.讲一下B树,B+树,哈希表,AVL树,红黑树?

这2篇介绍了红黑树,B树,B+树,讲的还可以:为什么要使用红黑树,B树和B+树_-停泊的博客-CSDN博客_索引 为什么用红黑树

B树、B+树、红黑树_prefect_start的博客-CSDN博客_b树 b+树 红黑树

这篇文章看完了,写的比较全面:面试题

7.怎么保证HashMap的线程安全?

通过ConcurrentHashMap线程安全类实现:JDK1.7通过对segment加锁,片段锁。JDK1.8降低了锁的粒度,通过对头结点加锁来保证线程安全的。

看完理论这篇博客,讲的还可以:ConcurrentHashMap是如何保证线程安全的?_Tom弹架构的博客-CSDN博客

8. CAS(Compare And Swap) 、synchronized、volatile?

1、 synchronized 是悲观锁,属于抢占式,会引起其他线程阻塞。

2、 volatile 提供多线程共享变量可见性和禁止指令重排序优化。

3、 CAS 是基于冲突检测的乐观锁(非阻塞)。

参考这篇,写的很好:基础篇:详解锁原理,synchronized、volatile+cas底层实现 - 知乎

9.ThreadLocal是否可保证资源的同步?它会导致内存泄漏?

  • 当使用ThreadLocal声明变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本
  • 从上面的概念可知,ThreadLocal其实并不能保证变量的同步性,只是给每一个线程分配一个变量副本
  • hreadLocalMap 是使用 ThreadLocal 的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。
  • 每次使用完ThreadLocal,都调用它的remove()方法,清除数据,防止内存泄漏。

参考这篇博文:ThreadLocal使用注意:线程不安全,可能会发生内存泄漏_深山猿的博客-CSDN博客_threadlocal有什么隐患

10.jvm 垃圾回收是什么时候触发?

参考这篇博客:jvm :垃圾回收是什么时候触发? 垃圾回收算法? 有哪些垃圾回收器?_花和尚也有春天的博客-CSDN博客

11.线程池有哪几种类型?

1、newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理所需,可灵活回收空闲线程,若线程数不够,则新建线程。

2、newFixedThreadPool:创建一个固定大小的线程池,即指定线程数的线程。可控制并发的线程数量,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

3、newSingleThreadExecutor:创建一个单线程的线程池,即只创建唯一的工作者线程来执行任务,,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

4、newScheduleThreadPool:创建一个定长的线程池,支持定时及周期性任务执行。  

推荐通过 new ThreadPoolExecutor() 的写法创建线程池,这样写线程数量更灵活开发中多数用这个类创建线程。

12.TCP三次握手四次挥手?

TCP的三次握手和四次挥手实质就是TCP通信的连接和断开。

三次握手:为了对每次发送的数据量进行跟踪与协商,确保数据段的发送和接收同步,根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系,并建立虚连接。

四次挥手:即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。

三次握手过程详细说明:

1、客户端发送建立TCP连接的请求报文,其中报文中包含seq序列号,是由发送端随机生成的,并且将报文中的SYN字段置为1,表示需要建立TCP连接。(SYN=1,seq=x,x为随机生成数值);

2、服务端回复客户端发送的TCP连接请求报文,其中包含seq序列号,是由回复端随机生成的,并且将SYN置为1,而且会产生ACK字段,ACK字段数值是在客户端发送过来的序列号seq的基础上加1进行回复,以便客户端收到信息时,知晓自己的TCP建立请求已得到验证。(SYN=1,ACK=x+1,seq=y,y为随机生成数值)这里的ack加1可以理解为是确认和谁建立连接;

3、客户端收到服务端发送的TCP建立验证请求后,会使自己的序列号加1表示,并且再次回复ACK验证请求,在服务端发过来的seq上加1进行回复。(SYN=1,ACK=y+1,seq=x+1)。
 

四次挥手​​​​​​​过程详细说明:

1、客户端发送断开TCP连接请求的报文,其中报文中包含seq序列号,是由发送端随机生成的,并且还将报文中的FIN字段置为1,表示需要断开TCP连接。(FIN=1,seq=x,x由客户端随机生成);

2、服务端会回复客户端发送的TCP断开请求报文,其包含seq序列号,是由回复端随机生成的,而且会产生ACK字段,ACK字段数值是在客户端发过来的seq序列号基础上加1进行回复,以便客户端收到信息时,知晓自己的TCP断开请求已经得到验证。(FIN=1,ACK=x+1,seq=y,y由服务端随机生成);

3、服务端在回复完客户端的TCP断开请求后,不会马上进行TCP连接的断开,服务端会先确保断开前,所有传输到A的数据是否已经传输完毕,一旦确认传输数据完毕,就会将回复报文的FIN字段置1,并且产生随机seq序列号。(FIN=1,ACK=x+1,seq=z,z由服务端随机生成);

4、客户端收到服务端的TCP断开请求后,会回复服务端的断开请求,包含随机生成的seq字段和ACK字段,ACK字段会在服务端的TCP断开请求的seq基础上加1,从而完成服务端请求的验证回复。(FIN=1,ACK=z+1,seq=h,h为客户端随机生成)
至此TCP断开的4次挥手过程完毕。
参考这篇博客:一文搞懂TCP的三次握手和四次挥手_不脱发的程序猿的博客-CSDN博客_三次握手和四次挥手

注意:TCP两次握手行不行?

因为TCP协议是双向的,第三次握手是为了使得sever知道客户答应了连接的请求。其中两次握手只能确定从客户端到服务端的网络是可达的,但却无法保证从服务端到客户端的网络是可达的。所以我们一定要保证双向的可达。

还有一个重要的原因:seq(序号)的初始化,要互相确定两方互相开始发送的序号,便于之后的发送、控制、纠错等等。

假如我们使用两次握手,则我们可以设想这么一种情况:客户向服务器进行请求,服务器同意并分配资源,向客户也发送信息,但此时这条信息因为网络原因丢失,客户便不了解服务器收没收到它发出的信息,则客户会继续向服务器发出请求,服务器若接收到,会认为是别的机器进行请求,继续分配资源,且也依然无法建立从服务器到客户的连接。
 

13.OSI七层和TCP/IP四层?OSI五层?

看下面一个图就可以大概了解了,如下:应用层、表示层、会话层可以归为应用层,数据链路层和物理层可以归结为网络接口层。

参照这篇博客:OSI七层和TCP/IP四层、五层协议 - 一切随风

14.算法题:两个有序链表合并?

思路1:迭代法,定义一个头节点为0的,依次遍历合并即可,最后再把头节点去掉。

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        //迭代法
        if(list1 == null){
            return list2 ;
        }
        if(list2 == null){
            return list1  ;
        }
        ListNode pHead = new ListNode(0) ;
        ListNode cur = pHead ;
        while(list1 != null && list2 != null){
            if(list1.val <= list2.val){
                cur.next = list1 ;
                list1 = list1.next ;
            }else{
                cur.next = list2 ;
                list2 = list2.next ;
            }
            cur = cur.next ;
        }
        //哪个链表还未结束,接在后面
        cur.next = list1 != null ? list1 : list2 ;
        //去掉头节点的0
        return pHead.next ;
        
    }
}

思路2:递归法。递归合并。

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        //递归法
        if(list1 == null){
            return list2 ;
        }
        if(list2 == null){
            return list1  ;
        }
        //若list1的值更小,则递归合并后接到list1上
        if(list1.val <= list2.val){
            list1.next = Merge(list1.next,list2) ;
            return list1 ;
        }else{//反之,递归合并接到list2上
            list2.next = Merge(list1,list2.next) ;
            return list2 ;
        }
        
    }
}

15.算法题:反转链表

题目链接:反转链表_牛客题霸_牛客网

思路1:在遍历链表时,将当前节点的next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        ListNode pre = null ;
        while(head != null){
            //先定义下一个指针,防止被覆盖
            ListNode head_next = head.next ;
            //让当前元素指向pre
            head.next = pre ;
            //更新pre
            pre = head ;
            //更新当前元素
            head = head_next ;
        }
        return pre ;
    }
}

递归法:每次递归反转两个,如下:

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
      //递归法
      if(head == null || head.next == null){
        return head ;
      }
      ListNode ans = ReverseList(head.next) ;
      head.next.next = head ;
      head.next = null ;
      return ans ;

    }
}

猜你喜欢

转载自blog.csdn.net/nuist_NJUPT/article/details/129190234