topcode【随机算法题】【2026.5.17打卡-java版本】

240. 搜索二维矩阵 II

要点:二分

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        //二分

        int n = matrix.length;
        int m = matrix[0].length;

        for(int i = 0; i < n; i++){
            if(target < matrix[i][0]){
                return false;
            }

            if(target > matrix[i][m-1]){
                continue;
            }

            int left = 0;
            int right = m-1;
            while(left <= right){
                int mid = left + (right - left)/2;
                if(matrix[i][mid] == target){
                    return true;
                }else if(matrix[i][mid] > target){
                    right = mid -1;
                }else{
                    left = mid + 1;
                }
            }
        }

        return false;        
    }
}

98. 验证二叉搜索树

要点:中序遍历,pre

java 复制代码
/**
 * 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 boolean isValidBST(TreeNode root) {
        //中序遍历

        Deque<TreeNode> stack = new ArrayDeque<>();

        //stack.push(root);

        Long pre =- Long.MAX_VALUE;

        while(!stack.isEmpty() || root != null){
            
            while(root!= null){
                stack.push(root);
                root = root.left;
            }

            TreeNode node = stack.pop();

            if(pre >= node.val){
                return false;
            }

            pre = (long)node.val;

            root = node.right;

        }

        return true;


        
    }
}

144. 二叉树的前序遍历

要点:栈,pushleft,pop, right

java 复制代码
/**
 * 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 List<Integer> preorderTraversal(TreeNode root) {
        //前序遍历->栈
                List<Integer> ans = new ArrayList<>();
        Deque<TreeNode> stack = new ArrayDeque<>();

        while(!stack.isEmpty() || root != null){

            while(root!= null){
                ans.add(root.val);
                stack.push(root);
                root = root.left;
            }

            TreeNode node = stack.pop();
           // ans.add(node.val);

            root = node.right;
        }

        return ans;
 
        
    }
}

662. 二叉树最大宽度

要点:层次+编号

javascript 复制代码
/**
 * 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 widthOfBinaryTree(TreeNode root) {
        //队列
        Deque<TreeNode> queue = new LinkedList<>();
        //编号
        LinkedList<Integer> list = new LinkedList<>();
        int max = 1;
        queue.offer(root);
        list.add(1);
        while(!queue.isEmpty()){
            int size = queue.size();
            //max = Math.max(size, max);
            for(int i = 0; i < size; i++){
                TreeNode tmep = queue.poll();
                Integer curInt = list.removeFirst();
                if(tmep.left != null){
                    queue.offer(tmep.left);
                    list.add( curInt *2);
                }

                if(tmep.right != null){
                    queue.offer(tmep.right);
                    list.add(curInt *2 +1);
                }

            }

            if(list.size() >= 2){
                max = Math.max(max, list.getLast() - list.getFirst() + 1);
            }
        }
         
         return max;
        
    }
}

随机知识

索引的底层实现(追问高频)

为什么 MySQL InnoDB 选择 B+ 树而不是哈希表或二叉树?

面试官为什么这么问?

这道题把数据结构的课内知识和工程选型串起来。我要看你能不能结合磁盘 IO 和实际查询场景来解释选型原因,而不是只会背"B+ 树好"。

希望听到怎样的回答:

  • 二叉树:数据量大时树太深,每次查都可能多次磁盘 IO,不适合磁盘存储。
  • 哈希表:等值查询 O(1) 快,但不支持范围查询、排序,无法利用索引做 ORDER BY 或 BETWEEN。
  • B+ 树:每个节点能存多个键值,树的高度非常低(通常 3-4 层),极大减少磁盘 IO;叶子节点有序双向链表,支持高效范围扫描和排序。这正是 MySQL 最常见的查询类型(范围查、排序、分页)。

候选人

好的,这个问题本质上是在问"数据库索引这种数据结构,核心要解决什么问题"。我按哈希表、二叉树、B树、B+树的顺序逐一分析,说明为什么 B+ 树是最优选择。

第一,为什么不用哈希表。

哈希表的最大特点是等值查询极快,时间复杂度 O(1),理论上单条数据查询性能比 B+ 树更好。MySQL 中也确实提供了哈希索引,在 Memory 引擎里可用,InnoDB 的自适应哈希索引也会自动构建哈希索引来加速热点页面的查询。

但哈希表作为通用索引结构有四个致命缺陷:

不支持范围查询。 WHERE id > 10 AND id < 100 这种条件,哈希表完全无法处理。因为哈希值之间没有顺序关系,数据被散列在各个桶里,彼此没有任何排序关联,只能全表扫描。

不支持排序和分组。 ORDER BYGROUP BY 需要数据有序排列,哈希表无法提供顺序遍历能力,必须额外排序,开销巨大。

哈希冲突问题。 数据量大的时候,哈希冲突不可避免。冲突严重时,桶里的链表变长,查询退化到 O(n)。虽然可以通过扩容缓解,但扩容需要重新计算所有 key 的哈希值并迁移数据,代价高昂。

内存与磁盘的适配性差。 哈希表设计更适合内存场景,数据均匀随机分布。数据库索引存储在磁盘上,随机 IO 的性能远低于顺序 IO,哈希表这种随机访问模式在磁盘上性能很差。

所以哈希表只在特定的等值查询场景下有优势,无法胜任 MySQL 最常用的范围查询、排序、分组等需求。

第二,为什么不用二叉树。

二叉树结构简单,但数据存储在磁盘时,它有严重的性能问题。

数据库数据以页为单位存储在磁盘上,每个节点保存在不同页中,访问一个节点就是一次磁盘 IO。二叉树每个节点只有两个子节点,1000 万条数据,树的高度至少是 log₂(10⁷) ≈ 24 层。一次查询需要 24 次磁盘 IO,性能完全不可接受。

即使使用 AVL 或红黑树这类平衡二叉树,每个节点依然只有两个子节点,树的高度问题没有本质改善。二叉树的"矮胖"程度受限于每个节点只能存一个键值,磁盘 IO 次数直接和树高成正比,这就是二叉树不适合磁盘存储的根本原因。

第三,B 树的改进与不足。

B 树是多路平衡树,每个节点可以包含多个键值,拥有多个子节点。这让树变得非常"矮胖",高度极大降低。1000 万数据用 B 树,每个节点存几百个键值,高度只需 3~4 层,磁盘 IO 锐减。

但 B 树有一个关键设计:数据分散在所有节点中,非叶子节点既存索引也存数据行。每条数据行可能几百甚至上千字节,而索引键通常只有几个字节。非叶子节点存了数据行之后,能容纳的键值数量剧烈减少,树的分叉数降低、高度增加。而且范围查询时需要在各层之间跳来跳去,效率不够高。

第四,B+ 树的最终胜出。

B+ 树在 B 树基础上做了两个关键优化,精准匹配数据库需求。

优化一:非叶子节点只存键值,不存数据。 所有数据全部下沉到叶子节点。非叶子节点只承担"路标"作用,每个 16KB 的节点页可以放入大量键值(比如一个 bigint 键值 8 字节加指针 6 字节,一个节点可存上千个键值),树的分叉数极大,高度极低。千万级数据 3 层完全覆盖,一次查询 1~3 次磁盘 IO 就能完成。

优化二:叶子节点形成有序双向链表。 所有数据按键值顺序存在叶子节点中,用双向指针串联。等值查询快速定位到目标节点,范围查询只需沿链表顺序遍历,磁盘顺序 IO 性能极高,完美支持 BETWEENORDER BYGROUP BY、分页查询这些 MySQL 最常见的操作。

在 InnoDB 里,聚簇索引的 B+ 树叶子节点直接存放完整行数据,一个索引就包含了全部字段,避免了回表。非聚簇索引的叶子节点存放索引列的值加主键值,查到主键后再回聚簇索引取整行数据。

另外,InnoDB 还有自适应哈希索引机制:当某些数据页的访问模式显示等值查询非常频繁,InnoDB 会在内存中为这些页自动建哈希索引,后续查询直接走哈希 O(1),完全自动无需手动配置。这是对 B+ 树的补充,体现了工程优化。

第五,一个实际场景说明。

面试官追问时,可以用项目例子佐证。用户表的手机号字段建了唯一索引,Hash 索引能快速判定手机号是否已存在,但这不够------业务里还需要查出该手机号的用户详细信息和最近登录时间,要求范围查询或排序,只有 B+ 树可以同时支撑。订单列表按时间范围查询并排序分页,也是完全依赖 B+ 树的叶子节点双向链表来完成高效的顺序扫描和翻页。

总结一句话:哈希表等值查询快但不支持范围和排序,二叉树太深磁盘 IO 高,B 树数据分散导致分叉不足,B+ 树通过非叶子只存键值和叶子双向链表两重优化,让树极矮且支持高效顺序扫描,完美匹配 MySQL 磁盘 IO 和范围查询的多重需求。

碎碎念:后续会更新每天学习的八股和算法 题,暑假实习找不到了,开始准备秋招的第7天。努力连续更新100天!今天确实又没咋学,感觉一放假就不能正常学习,要坚持鸭,不要自己放纵自己。多坚持一下哇!!!!不管心情怎么样,都要按计划做。

相关推荐
Geometry Fu1 小时前
《设计模式》2026编程作业汇总
java·c++·设计模式
YOU OU1 小时前
Spring MVC 练习项目
java·后端·spring
吃好睡好便好1 小时前
在Matlab中绘制柱面图
开发语言·学习·算法·matlab
沐怡旸1 小时前
彻底告别解析崩溃:深入解析大模型 Structured Outputs(结构化输出)技术
算法
ChoSeitaku1 小时前
02.变量_数据类型转换_运算符
java·大数据·开发语言
giszz1 小时前
量子算法简化解析:肖尔算法与格罗弗算法核心原理
算法·量子计算
微风欲寻竹影1 小时前
队列(Queue)-详解
java·数据结构
想不明白的过度思考者1 小时前
Unity全局事件中心与新版输入架构实现练习——上帝模式与英雄模式的输入系统映射切换
java·unity·架构
小新同学^O^2 小时前
简单学习Spring原理
java·学习·spring