695.岛屿的最大面积
要点:bfs!! 标记要变1->0
java
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
int max = 0;
for(int i = 0; i < n; i++){
for(int j =0; j < m; j++){
if(grid[i][j] == 1){
int ans = bfs(i, j, grid);
max = Math.max(ans, max);
}
}
}
return max;
}
public int bfs(int n, int m, int[][] grid){
if(n < 0 || n >= grid.length || m < 0 || m >= grid[0].length || grid[n][m] == 0 ){
return 0;
}
grid[n][m] =0;
int[][] dirs = new int[][]{{1,0}, {-1,0}, {0,1}, {0,-1}};
int res = 1;
for(int[] dir : dirs){
res += bfs(n+dir[0], m+dir[1], grid);
}
return res;
}
}
122.买卖股票的最佳时机
贪心
java
class Solution {
public int maxProfit(int[] prices) {
int max = 0;
for(int i = 1; i < prices.length; i++){
if(prices[i] > prices[i-1]){
max += prices[i] - prices[i-1];
}
}
return max;
}
}
110.平衡二叉树
dfs
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 {
//dfs
public boolean isBalanced(TreeNode root) {
return getHeight(root) != -1;
}
public int getHeight(TreeNode root){
if(root == null){
return 0;
}
int left = getHeight(root.left);
int right = getHeight(root.right);
if(left == -1 || right == -1 || Math.abs(left-right) > 1){
return -1;
}
return Math.max(left, right) +1;
}
}
234.回文链表
要点:中间点+反转链表
java
/**
* 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 boolean isPalindrome(ListNode head) {
ListNode mid = middle(head);
ListNode head2 = reverse(mid);
while(head2 != null){
if(head.val == head2.val){
head = head.next;
head2 = head2.next;
}else{
return false;
}
}
return true;
}
public ListNode middle(ListNode head){
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
public ListNode reverse(ListNode head){
//要是null
ListNode pre = null;
ListNode curr = head;
while(curr != null){
ListNode next = curr.next;
curr.next = pre;
pre =curr;
curr =next;
}
return pre;
}
}
随机基础知识
一、索引(必问,最高频)
索引是什么?为什么要用 B+ 树?联合索引的最左前缀原则是怎么回事?
面试官为什么这么问?
索引是数据库优化的核心,我把它放第一题就是要看你是否真正理解数据结构的选择,而不是背一个"索引就像书的目录"。我想听你说清为什么 MySQL 选择了 B+ 树,以及怎么写 SQL 才能用上你建的索引。
希望听到怎样的回答:
- 索引是帮助 MySQL 高效查询的数据结构,类比书的目录。
- 为什么用 B+ 树:非叶子节点只存索引,不存数据,树更矮胖,减少磁盘 IO ;叶子节点形成有序双向链表,范围查询高效。
- 联合索引的最左前缀原则:索引 (a, b, c) 相当于建了 (a)、(a,b)、(a,b,c) 三个索引;查询条件缺最左边的列,索引失效。
- 举例:
where b=1不走索引;where a=1 and c=2只会用到 a,c 用不了;where a=1 and b>2 and c=3中 c 也用不了,因为范围查询会截断。 - 能加一句"我们项目里用户表的手机号建了唯一索引,面试题目的分类和难度字段建了联合索引"会非常加分。
候选人 :
好的,我从这三个问题依次展开讲。
第一部分:索引是什么。
索引是数据库表中一种用于加速数据检索的数据结构。类比书的目录,没有目录要查一个关键词得逐页翻整本书,有了目录可以直接定位到对应的页码。在 MySQL 中,索引同样避免全表扫描,通过数据结构快速定位到目标数据所在的磁盘位置,显著减少磁盘 IO 次数,提升查询性能。索引本身存储在磁盘上,有自己的文件结构。
第二部分:为什么用 B+ 树。
MySQL 选择 B+ 树而不是哈希表或二叉树,核心原因有两个:磁盘 IO 优化 和范围查询支持。
先看哈希表。哈希表对等值查询非常快,O(1) 时间就能定位数据。但它有两个致命问题:一是不支持范围查询,where id > 10 and id < 100 这种条件,哈希表只能全表扫描,因为哈希值之间没有顺序关系;二是哈希冲突严重时性能下降。数据库查询中最常见的不是单条精确查询,而是范围查询和排序,所以哈希表不适合。
再看二叉树。二叉树在数据有序插入时会退化成链表,查询从 O(log n) 变成 O(n)。即使平衡二叉树如红黑树可以自平衡,但每个节点只有两个子节点,当数据量达千万级别时,树的高度会非常大。树的高度直接影响磁盘 IO 次数,因为数据库中每个节点存储在磁盘的不同页中,访问一个节点就是一次 IO,高度越大性能越差。
B 树是多路平衡查找树,每个节点可以有多个子节点,树的高度远低于二叉树。但 B 树在非叶子节点中也存储数据,导致每个节点能存的索引数量变少,树的分叉数降低、高度增加。并且 B 树在范围查询时需要在各层之间来回遍历,效率较差。
B+ 树对 B 树的改进正是针对这两点。第一,B+ 树的非叶子节点只存储键值(索引列的值)和子节点指针 ,不存储实际数据行。这样一个节点可以容纳很多索引条目,树的分叉数极大,高度变得非常矮。千万级数据的 B+ 树高度通常只有 3~4 层,一次查询只需 3~4 次磁盘 IO。第二,B+ 树所有数据记录都存放在叶子节点中,叶子节点之间用双向链表串联。等值查询可以快速到达叶子节点,范围查询只需一次定位到起始位置,然后顺着链表向后遍历即可,效率非常高。
所以在 MySQL 的 InnoDB 引擎中,表根据主键构建聚簇索引,就用 B+ 树。聚簇索引的叶子节点直接存放整行数据。非聚簇索引的叶节点存放的是索引列的值加主键值,需要回表取完整行数据。
第三部分:联合索引的最左前缀原则。
联合索引是最常在面试中被追问的索引类型。它在单个索引中包含多个列,索引按定义的列顺序构建 B+ 树。比如定义一个联合索引 (a, b, c),B+ 树的节点先按 a 排序,a 相同时按 b 排序,b 相同时再按 c 排序。
最左前缀原则规定:联合索引 (a, b, c) 其实相当于创建了 (a)、(a, b)、(a, b, c) 三个索引,但不能跳过最左边的列。具体来说:
where a = 1可以走索引,匹配列 a。where a = 1 and b = 2可以走索引,匹配 a 和 b。where a = 1 and b = 2 and c = 3可以走索引,匹配所有三列。where b = 2无法走索引,因为缺少最左列 a。where b = 2 and c = 3同样无法走索引。where a = 1 and c = 3只能用到 a 这个索引,c 用不了,因为中间跳过了 b。
范围查询截断问题 :当最左前缀匹配到范围查询时,后续的列会失效。where a = 1 and b > 2 and c = 3 中,索引只能用到 a 和 b,c 用不了。因为 b 使用了范围条件,B+ 树在 a 相同的分组内按 b 排序,但 b 的范围匹配出多条记录后,这些记录的 c 是分散不连续的,无法利用 b 后面的 c 列继续快速定位。这就是典型的最左前缀匹配中断。
所以在创建联合索引时,把等值查询条件中的最常用字段放在最左侧,同时在涉及范围查询的字段后面不再附加其它等值过滤字段。查询条件的顺序不影响索引使用,MySQL 优化器会自动调整 WHERE 条件内部顺序来匹配最左前缀,关键是索引列定义的顺序是否完整包含了查询条件的最左边部分。
第四部分:项目中的实际应用。
我项目里的用户表在手机号字段上建了唯一索引,保证手机号全局不重复,同时支持通过手机号快速查询用户。面试题目的表在分类和难度字段上建了联合索引 (category, difficulty),因为所有查询列表的入口条件都是按分类筛选再按难度排序,建立这个联合索引后查询时间从几百毫秒降到毫秒级别。
设计索引时会综合考量字段的区分度和查询频率,高区分度的字段放在前面能快速缩小数据范围。
总结一句话:索引是用空间换时间的查询优化方案,MySQL 选择 B+ 树是因为它的矮胖结构减少磁盘 IO,并且双向链表让范围查询同样高效;联合索引按最左前缀原则使用,跳过最左列就无法利用索引,范围查询会截断后续列的索引使用。项目里根据查询入口和字段区分度来设计索引,是数据库优化最基本也是最有效的手段。
碎碎念:后续会更新每天学习的八股和算法 题,暑假实习找不到了,开始准备秋招的第6天。努力连续更新100天!今天确实没学啥,玩了一天,不管适当休息就当,加油加油!!把大体思路重新梳理了一遍。严格按计划学习吧。不要觉得休息日就放松警惕了!!!!