Java集训(算法&&面试题)第四天 (链表 & 树)

468 阅读4分钟

「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战」。

导读

在这里插入图片描述

在刚刚结束的 每日算法&面试题,大厂特训二十八天冲刺大厂每日算法&面试题,动态规划21天 的训练中我们一起打卡走了过来。但是学习不能停呀,从今天开始我们开始Java集训(算法&&面试题)第一天接着卷起来。

Java集训

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 34,我们返回第二个结点。
 

思路:链表的缺点在于不能通过下标访问对应的元素。因此我们可以考虑对链表进行遍历,同时将遍历到的元素依次放入数组 A 中。如果我们遍历到了 N 个元素,那么链表以及数组的长度也为 N,对应的中间节点即为 A[N/2]。

初始化:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */

解题部分:

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode[] A = new ListNode[100];
        int t = 0;
        while (head != null) {
            A[t++] = head;
            head = head.next;
        }
        return A[t / 2];
    }
}


给定二叉树的根节点 root ,返回所有左叶子之和。

在这里插入图片描述

示例 1:



输入: root = [3,9,20,null,null,15,7] 
输出: 24 
解释: 在这个二叉树中,有两个左叶子,分别是 915,所以返回 24
示例 2:

输入: root = [1]
输出: 0

初始部分:
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
 
解题部分:
深度优先搜索

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        return root != null ? dfs(root) : 0;
    }

    public int dfs(TreeNode node) {
        int ans = 0;
        if (node.left != null) {
            ans += isLeafNode(node.left) ? node.left.val : dfs(node.left);
        }
        if (node.right != null && !isLeafNode(node.right)) {
            ans += dfs(node.right);
        }
        return ans;
    }

    public boolean isLeafNode(TreeNode node) {
        return node.left == null && node.right == null;
    }
}


面试题

自己写过 String 类能加载吗,之前的 String 是什么时候加载进去的?
不能加载,因为双亲委派机制,JVM 出于安全性的考虑,全限定类名相同的 String 是不能
被加载的。
java.lang.String 会被顶级类加载器 Bootstrap Classloader 加载。当 class 文件被加载到
内存中时,类文件常量池中的其他常量会加载到运行时常量池,但是字符串常量不会。它会首
先在堆区中创建一个字符串对象,然后再把这个对象的引用保存到全局字符串常量池中。



、描述 ThreadLocal(线程本地变量)的底层实现原理及常用场景?
实现原理:
 每个 Thread 线程内部都有一个 ThreadLocalMap;以线程作为 key,泛型作为 value,
可以理解为线程级别的缓存。每一个线程都会获得一个单独的 map。
 提供了 set 和 get 等访问方法,这些方法为每个使用该变量的线程都存有一份独立的副
本,因此 get 方法总是返回由当前执行线程在调用 set 时设置的最新值。
应用场景:
 JDBC 连接
 Session 管理
 Spring 事务管理
 调用链,参数传递
 AOP
ThreadLocal 是一个解决线程并发问题的一个类,用于创建线程的本地变量,我们知道一个
对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。
但是当我们不想使用同步的时候,我们可以选择 ThreadLocal 变量。例如,由于 JDBC 的
连接对象不是线程安全的,因此,当多线程应用程序在没有协同的情况下,使用全局变量时,
就不是线程安全的。通过将 JDBC 的连接对象保存到 ThreadLocal 中,每个线程都会拥有
属于自己的连接对象副本