leecodecode【面试150】【2026.7.3打卡-java版本】

实现 Trie (前缀树)

要点:26叉树

java 复制代码
class Trie {
    //26叉树
    private static class Node{
        Node[] son = new Node[26];
        boolean end = false;
    }



    private final Node root = new Node();

    public Trie() {
        
    }
    
    public void insert(String word) {
        Node cur = root;
        for(char c : word.toCharArray()){
            int index = c - 'a';
            if(cur.son[index] == null){
                cur.son[index] = new Node();
            }
            cur = cur.son[index];
        }
        cur.end = true;
        
    }
    
    public boolean search(String word) {
        return find(word) == 2;

        
    }
    
    public boolean startsWith(String prefix) {
        return find(prefix) != 0;
        
    }

    public int find(String word){
        Node cur =root;
        for(char c : word.toCharArray()){
            int index = c - 'a';
            if(cur.son[index] == null){
                return 0;
            }
            cur = cur.son[index];
        }

        return cur.end? 2:1;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */

添加与搜索单词 - 数据结构设计

要点:同上

java 复制代码
class WordDictionary {
    //26叉树
    private static class Node{
        Node[] son = new Node[26];
        boolean end = false;
    }

    private final Node root;

    public WordDictionary() {
        root = new Node();
        
    }
    
    public void addWord(String word) {
        Node cur = root;
        
        for(char c : word.toCharArray()){
            int index = c - 'a';
            if(cur.son[index] == null){
                cur.son[index] = new Node();
            }
            cur = cur.son[index];
        }

        cur.end = true;
        
    }
    
    public boolean search(String word) {
        return dfs(root, word, 0);
        
    }

    private boolean dfs(Node cur, String word, int index){
        if(index == word.length()){
            return cur.end;
        }

        char ch = word.charAt(index);

        if(ch == '.'){
            for(Node child : cur.son){
                if(child != null && dfs(child, word, index +1)){
                    return true;
                }
            }
            return false;
        }else{
            int idx = ch - 'a';
            if(cur.son[idx] == null){
                return false;
            }

            return dfs(cur.son[idx], word, index+1);
        }
    }
}

/**
 * Your WordDictionary object will be instantiated and called as such:
 * WordDictionary obj = new WordDictionary();
 * obj.addWord(word);
 * boolean param_2 = obj.search(word);
 */

组合总和

要点:回溯

java 复制代码
class Solution {
    List<List<Integer>> anss = new ArrayList<>();
    //如何去重
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        //bfs回溯
        
        List<Integer> ans = new ArrayList<>();
        int n = candidates.length;
        boolean[] used = new boolean[n];
        dfs(ans, candidates, used,0,target);
        return anss;
        
    }

    public void dfs(List<Integer> ans, int[] candidates, boolean[] used, int index, int target){
        if(target == 0){
            List<Integer> temp = new ArrayList<>(ans);
            Collections.sort(temp);
            anss.add(temp);
            return;
        }

        if(target <0){
            return;
        }

        for(int i = index; i < candidates.length; i++){
            
            ans.add(candidates[i]);
            dfs(ans,candidates,used,i,target-candidates[i]);
            ans.removeLast();
          
    
        }
    }
}

N 皇后 II

要点:

java 复制代码
class Solution {
    private int ans;

    public int totalNQueens(int n) {
        boolean[] col = new boolean[n];
        boolean[] diag1 = new boolean[n * 2 - 1];
        boolean[] diag2 = new boolean[n * 2 - 1];
        dfs(0, col, diag1, diag2);
        return ans;
    }

    private void dfs(int r, boolean[] col, boolean[] diag1, boolean[] diag2) {
        int n = col.length;
        if (r == n) {
            ans++; // 找到一个合法方案
            return;
        }
        for (int c = 0; c < n; c++) {
            int rc = r - c + n - 1;
            if (!col[c] && !diag1[r + c] && !diag2[rc]) {
                col[c] = diag1[r + c] = diag2[rc] = true;
                dfs(r + 1, col, diag1, diag2);
                col[c] = diag1[r + c] = diag2[rc] = false; // 恢复现场
            }
        }
    }
}

单词搜索 II

java 复制代码
class Solution {
    // 1. 定义 Trie 节点
    private static class Node {
        Node[] children = new Node[26];
        String word = null;          // 直接存储完整单词,方便添加结果
    }

    // 2. 构建 Trie
    private Node buildTrie(String[] words) {
        Node root = new Node();
        for (String w : words) {
            Node cur = root;
            for (char c : w.toCharArray()) {
                int idx = c - 'a';
                if (cur.children[idx] == null) {
                    cur.children[idx] = new Node();
                }
                cur = cur.children[idx];
            }
            cur.word = w;             // 单词结束处存储完整单词
        }
        return root;
    }

    // 3. 主函数
    public List<String> findWords(char[][] board, String[] words) {
        List<String> result = new ArrayList<>();
        Node root = buildTrie(words);
        int m = board.length, n = board[0].length;

        // 从每个格子开始 DFS
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                dfs(board, i, j, root, result);
            }
        }
        return result;
    }

    // 4. DFS 回溯
    private void dfs(char[][] board, int i, int j, Node node, List<String> result) {
        char c = board[i][j];
        // 如果当前字符不是 node 的孩子,直接返回(剪枝)
        int idx = c - 'a';
        if (node.children[idx] == null) {
            return;
        }

        node = node.children[idx];   // 沿着 Trie 往下走

        // 如果当前节点是某个单词的结尾,加入结果
        if (node.word != null) {
            result.add(node.word);
            node.word = null;        // 防止重复添加同一个单词
        }

        // 标记当前格子已访问(用特殊字符)
        board[i][j] = '#';

        // 四个方向搜索
        int[] dx = {-1, 1, 0, 0};
        int[] dy = {0, 0, -1, 1};
        for (int d = 0; d < 4; d++) {
            int ni = i + dx[d];
            int nj = j + dy[d];
            if (ni >= 0 && ni < board.length && nj >= 0 && nj < board[0].length && board[ni][nj] != '#') {
                dfs(board, ni, nj, node, result);
            }
        }

        // 回溯,恢复当前格子字符
        board[i][j] = c;
    }
}

随机知识

一、集合框架概览(开篇必考)

1. 请说说Java集合的整体框架结构?

  • 两大体系 :Java集合主要分为Collection(存储单一元素)和Map(存储键值对)两大接口体系。

  • Collection子接口List(有序、可重复)、Set(无序、不可重复)、Queue(队列)。

  • Map实现类HashMapTreeMapLinkedHashMapHashtableConcurrentHashMap等。

  • 核心区别List重顺序,Set重唯一,Map重键值查找。

2. CollectionCollections有什么区别?

  • Collection是一个接口,是集合框架的根接口,定义了集合的基本操作方法。

  • Collections是一个工具类 ,提供了大量静态方法来操作或返回集合,如sort()synchronizedList()等。


二、List接口(高频:ArrayList vs LinkedList)

3. ArrayListLinkedList的区别?(几乎必考)

  • 底层数据结构ArrayList基于动态数组LinkedList基于双向链表

  • 随机访问性能ArrayList支持快速随机访问,时间复杂度O(1)LinkedList需遍历,时间复杂度O(n)

  • 插入/删除性能ArrayList在中间位置插入删除需移动元素,效率低(O(n));LinkedList只需修改节点引用,效率高(O(1))。

  • 内存占用ArrayList内存更紧凑;LinkedList每个节点需额外存储两个指针,内存开销更大。

  • 适用场景 :读多写少、随机访问用ArrayList;写多读少、频繁中间插入删除用LinkedList

  • 线程安全 :两者都是线程不安全的。

4. ArrayList的扩容机制是怎样的?

  • JDK 8中采用懒加载new ArrayList()时初始为空数组,第一次add时才初始化为容量10。

  • 当元素数量超过当前容量时触发扩容,新容量约为原来的1.5倍 (通过Arrays.copyOf()复制数组)。

5. VectorArrayList的区别?为什么不推荐用Vector

  • Vector是线程安全的(方法用synchronized修饰),但采用粗粒度锁导致性能较差。

  • ArrayList线程不安全但性能更好。

  • 现代替代方案:单线程用ArrayList;需要线程安全可用CopyOnWriteArrayListCollections.synchronizedList()


三、Set接口(去重与排序)

6. HashSetLinkedHashSetTreeSet的区别?

  • HashSet :基于HashMap实现,无序 ,允许一个null,性能最高。

  • LinkedHashSet :继承HashSet,内部用LinkedHashMap保持插入顺序

  • TreeSet :基于红黑树 实现,自动排序 (自然排序或定制排序),不允许null

  • 共同点:元素都不重复。

7. HashSet如何保证元素不重复?

  • 依赖元素的hashCode()equals()方法。

  • 先计算哈希值定位桶;若该位置无元素则直接存入;若有元素则用equals()比较,false则以链表/红黑树形式存储。

  • 重要原则 :重写equals()必须重写hashCode(),否则可能破坏唯一性。


四、Map接口(重中之重:HashMap)

8. HashMap的底层数据结构是什么?JDK 7和JDK 8有何区别?

  • JDK 7数组 + 链表(拉链法解决哈希冲突)。

  • JDK 8数组 + 链表 + 红黑树 。当链表长度 > 8 数组容量 > 64时,链表转为红黑树,优化查询效率。

9. HashMapHashtableTreeMapLinkedHashMap的区别?

  • 线程安全Hashtable线程安全(方法加锁),其余均非线程安全

  • null键值HashMap允许一个null键和多个null值;Hashtable不允许null

  • 顺序TreeMap按键排序;LinkedHashMap保持插入或访问顺序;HashMap无序。

  • 底层HashMap数组+链表+红黑树;Hashtable类似但线程安全;TreeMap红黑树;LinkedHashMap继承HashMap + 双向链表。

10. HashMap的工作原理(put/get流程)?

  • put:计算key的哈希值 → 定位数组索引 → 若为空则创建节点;若存在则遍历链表/红黑树,找到相同key则替换value,否则插入新节点。

  • get:计算key哈希值定位索引 → 遍历链表/红黑树查找匹配key → 返回对应value。

  • 扩容时机:元素数量 > 容量 × 加载因子(默认0.75)时触发扩容。


五、其他重要概念

11. IteratorListIterator的区别?

  • Iterator可遍历ListSet,支持单向遍历及remove()

  • ListIterator仅用于List,支持双向遍历,并可添加、修改元素。

12. fail-fast(快速失败)和fail-safe(安全失败)机制?

  • fail-fast :遍历时若集合结构被修改(非迭代器自身remove),立即抛出ConcurrentModificationException

  • fail-safe :遍历时操作的是集合的副本,允许并发修改(如CopyOnWriteArrayList)。

13. 数组和集合的区别?

  • 数组长度固定 ;集合长度动态可变

  • 数组可存基本类型 和引用类型;集合只能存引用类型(基本类型需包装类)。

  • 数组元素类型必须相同;集合可存不同类型对象。


六、并发集合(进阶必问)

14. ConcurrentHashMap的实现原理?

  • JDK 8采用数组 + 链表 + 红黑树 ,与HashMap结构类似。

  • 通过CAS + synchronized 实现线程安全,锁粒度更细(仅锁住链表/红黑树的头节点),并发性能远优于Hashtable

15. ArrayList如何变成线程安全?

  • Collections.synchronizedList(list):全局锁包装,性能一般。

  • CopyOnWriteArrayList写时复制,读操作无锁,适合读多写少场景。

  • Vector:古老实现,全方法加锁,基本不推荐。


面试准备建议

面试官真正想看的,是你有没有思考过"为什么这么设计",以及能不能结合业务做优化。

建议重点掌握:

  1. 底层数据结构(数组、链表、红黑树)

  2. 核心方法的时间复杂度

  3. 线程安全问题及解决方案

  4. JDK版本差异(特别是HashMap在7和8中的变化)

以上覆盖了Java集合面试中80%以上的高频考点

碎碎念:后续会更新每天学习的八股和算法 题,开始准备秋招的第54天。努力连续更新100天!以后每天就按,秋招项目【java +agent】,科研,必做项目,算法,八股,锻炼身体来总结。

总结:慢慢恢复状态吧

1.算法面试150 104/150 2h

2.秋招项目,【java 项目】,

【agent 项目 】,

3.科研要跑一下,

4.实习;6h

6.背八股,1h

7.锻炼身体,

杂活好多,少玩手机