面试笔记:面经-猿辅导-一面


一、自我介绍

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


二、项目相关

1、React介绍。
2、SVM介绍。


三、Java后台

3.1 Java异常处理

3.1.1 Exception和Error的区别

Exception和Error都是继承于Throwable类

  • Exception:是java程序运行中可预料的异常情况,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
  • Error:是java程序运行中不可预料的异常情况,这种异常发生以后,会直接导致JVM不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。

3.1.2 RuntimeException和CheckedException的区别

RuntimeException和CheckedException都继承自Exception类

  • RuntimeException:运行时异常。也称作未检测异常(Unchecked Exception),这表示这种异常不需要编译器来检测。RuntimeException是所有可以在运行时抛出的异常的父类。一个方法除要捕获异常外,如果它执行的时候可能会抛出RuntimeException的子类,那么它就不需要用throw语句来声明抛出的异常。 例如:NullPointerException,ArrayIndexOutOfBoundsException等等
  • CheckedException:受检查异常。是编译器在编译时进行校验的,通过throws语句或者try{}cathch{}语句块来处理检测异常。编译器会分析哪些异常会在执行一个方法或者构造函数的时候抛出。

3.2 Java线程

3.2.1 sychronized

synchronized的特点是自动释放锁,作用在方法时自动获取锁,任意对象都可做为锁,它是最常用的加锁机制。
synchronized可以手动指定锁,当作用在方法时会自动获取锁:

  • 作用于普通方法获得当前对象锁,等价于synchronized(this)。
  • 作用于静态方法获得类锁,等价于synchronized(类.class)。

3.2.2 Lock

Lock的特点是必须自己创建锁(锁类型已经指定为Lock的实现类,不能使用其它对象),必须自己释放锁。
一定要在finally中释放锁,保证即便抛出异常也可以释放。

3.2.3 volatile

volatile是稍微弱一点的同步机制,主要就是用于将变量的更新操作通知到其它线程。volatile变量是一种比sychronized关键字更轻量级的同步机制。声明变量是volatile的,JVM保证每次读变量都从内存中读,跳过CPU cache这一步。


四、算法题(手撕)

4.1 以X为基准分割链表

以给定X为基准将链表分割为两部分,所有小于X的节点排在大于等于节点之前,并且保持原来顺序基本不变。

public static Node partition(Node head, int x) {
    if (head == null) {
        return null;
    }
    Node small = null;
    Node big = null;
    Node smallTail = null;
    Node bigTail = null;
    for (Node cur = head; cur != null; cur = cur.next) {
        if (cur.value < x) {
            if (small == null) {
                small = cur;
            } else {
                smallTail.next = cur;
            }
            smallTail = cur;
        } else {
            if (big == null) {
                big = cur;
            } else {
                bigTail.next = cur;
            }
            bigTail = cur;
        }
    }
    if (small == null) {
        return big;
    } else {
        smallTail.next = big;
        if (bigTail != null) {
            bigTail.next = null;
        }
        return small;
    }
}

4.2 逆时针打印二叉树边缘

public static void getLeftSizeNodes(TreeNode root) {//遍历左边缘节点
    TreeNode node = root;
    while (node != null) {
        list.add(node);
        node = node.left;
    }
}

public static void getBottomSizeNodes(TreeNode root) {//遍历底层叶子节点
    TreeNode node = root;
    if (node == null) {//根节点为空
        return;
    }
    getBottomSizeNodes(node.left);//递归根节点的左子树
    if (node.left == null && node.right == null) {//如果当前节点是叶子节点
        if (list.get(list.size() - 1) != node) {
            list.add(node);
        }
        return;
    }
    getBottomSizeNodes(node.right);//递归根节点的右子树
}


public static void getRightSizeNodes(TreeNode root) {//遍历右边缘节点
    TreeNode node = root;
    Stack<TreeNode> stack = new Stack<TreeNode>();//因为右边缘节点需要从下往上打印所以要用到栈

    node = node.right;
    while (node != null) {//遍历根节点的右子树及其右子树的右子树
        stack.push(node);//非空则压栈
        node = node.right;
    }
    while (!stack.isEmpty()) {
        TreeNode n = stack.pop();//出栈
        if (list.get(list.size() - 1 ) != n) {
            list.add(n);
        }
    }
}
发布了90 篇原创文章 · 获赞 10 · 访问量 4万+

猜你喜欢

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