二叉树的最大深度
思路1:利用递归将大问题转换为局部重复的子问题
如果递归到空节点,则返回深度为0
否则在每次递归返回的左子树最大深度和右子树最大深度中取最大值+1
/**
* 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 dfs(TreeNode node){
if(node==null)return 0;
return Math.max(dfs(node.left),dfs(node.right))+1;
}
public int maxDepth(TreeNode root) {
return dfs(root);
}
}
思路2:为避免层数过多导致栈溢出,可以考虑使用层序遍历
import java.util.LinkedList;
import java.util.Queue;
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0;
Queue queue = new LinkedList<>();
queue.offer(root);
int depth = 0;
while (!queue.isEmpty()) {
int size = queue.size(); // 当前层的节点数
// 把当前层的节点全部弹出,并将下一层的节点入队
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
depth++; // 每处理完一层,深度 +1
}
return depth;
}
}
翻转二叉树
思路:利用递归,将翻转根节点左右子树问题转换为多个反转任意节点左右子树的子问题
/**
* 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 TreeNode rev(TreeNode node){
if(node==null)return null;
if(node.leftnull&&node.rightnull){
return node;
}
TreeNode temp=node.left;
node.left=node.right;
node.right=temp;
rev(node.left);
rev(node.right);
return node;
}
public TreeNode invertTree(TreeNode root) {
return rev(root);a
}
}
对称二叉树
给你一个二叉树的根节点 root , 检查它是否轴对称
思路:利用BFS队列,每次取出一组判断节点,判断是否相等
核心:入队的顺序需要镜像入队
/**
* 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 isSymmetric(TreeNode root) {
if(node==null)return true;
Queue q=new LinkedList<>();
q.offer(root.left);
q.offer(root.right);
while(!q.isEmpty()){
TreeNode l=q.poll();
TreeNode r=q.poll();
if(lnull&&rnull)continue;
if(lnull||rnull||(l.val!=r.val))return false;
q.offer(l.left);
q.offer(r.right);
q.offer(l.right);
q.offer(r.left);
}
return true;
}
}
思路2:递归dfs判断镜像
/**
-
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 isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
// 比较根节点的左右两棵子树是否互为镜像
return check(root.left, root.right);
}
// 辅助函数:判断 left 和 right 两棵树是否互为镜像
private boolean check(TreeNode left, TreeNode right) {
// 1. 如果左右都为空,说明对称
if (left == null && right == null) {
return true;
}
// 2. 如果只有一个为空(或者两个节点的值不相等),说明不对称
if (left == null || right == null || left.val != right.val) {
return false;
}
// 3. 核心逻辑:外侧对比外侧,内侧对比内侧
// left.left 和 right.right 比较(外侧)
// left.right 和 right.left 比较(内侧)
return check(left.left, right.right) && check(left.right, right.left);
}
}
二叉树的直径
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。
思路:将问题拆成求任意节点的左右子树最大深度和,取其中最大值
要遍历所有节点+求树最大深度
遍历用BFS+树最大深度用DFS
/**
* 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 maxdepth(TreeNode node){
if(node==null)return 0;
return Math.max(maxdepth(node.left),maxdepth(node.right))+1;
}
public int diameterOfBinaryTree(TreeNode root) {
if(root==null)return 0;
Queue q=new LinkedList<>();
q.offer(root);
int max=-1;
while(!q.isEmpty()){
int n=q.size();
for(int i=0;i<n;i++){
TreeNode node=q.poll();
int cur=maxdepth(node.left)+maxdepth(node.right);
if(cur>max){
max=cur;
}
if(node.left!=null)q.offer(node.left);
if(node.right!=null)q.offer(node.right);
}
}
return max;
}
}
将有序数组转为二叉搜索树
思路:利用递归构建左右二叉搜索子树,每次取中点作为根,递归构造左子树和右子树
/**
* 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 TreeNode buildBST(int\[\] nums,int left,int right){
if(left>right)return null;
int mid=left+(right-left)/2;
TreeNode root=new TreeNode(numsmid);
root.left=buildBST(nums,left,mid-1);
root.right=buildBST(nums,mid+1,right);
return root;
}
public TreeNode sortedArrayToBST(int\[\] nums) {
if(nums==null)return null;
return buildBST(nums,0,nums.length-1);
}
}
1.怎么保障ai代码正确的
- **人工代码 Review **:AI 经常会生成看似合理但存在安全漏洞(如 SQL 注入、并发问题)或边界遗漏的代码。
- 小步快跑(DevOps):不要让 AI 一次性生成整个模块。按照功能点拆分,每次生成一个小函数或类,立马自测验证。
- 给AI提供极其明确的 Prompt(背景、约束、边界、输入输出格式)
2.虚拟地址和物理地址的区别
物理地址 :内存硬件(DRAM)真实的物理寻址地址。
虚拟地址:操作系统给每个进程假造的地址空间。
通过 MMU(内存管理单元) 结合页表(Page Table),将虚拟地址映射到物理地址。这样做带来了三大好处:
-
进程隔离 :进程 A 和进程 B 的虚拟地址
0x100映射到不同的物理内存,互相隔离,保证安全。 -
内存扩容:利用虚拟内存技术,将不常用的内存页(Page)换出到磁盘的 Swap 区,让程序可以使用超过物理内存大小的空间。
4.进程可以有一样的虚拟地址吗
-
进程 A 的虚拟地址
0x08048000和进程 B 的虚拟地址0x08048000可以完全相同。 -
但是,经过各自的页表映射后,它们会指向完全不同的物理内存页。
6.tcp可靠性靠什么保障
-
超时重传(Retransmission):发送端维护一个定时器(RTO),如果发出数据后在规定时间内没收到 ACK,就会认为丢包,自动重传。
-
连接管理(三次握手、四次挥手):确保通信双方都在线、状态同步,且能安全地释放资源。
-
流量控制(滑动窗口):接收端通过 TCP 头部的 Window 字段告诉发送端:"我还能接收多少数据",防止发送太快把接收端缓冲池打爆。
聚簇索引(Clustered Index)
-
概念 :聚簇索引不是一种单独的数据结构,而是数据的物理存储方式。在 InnoDB 中,表数据本身就是按 B+ 树组织的一棵树。
-
叶子节点 :存放的是完整的行数据(包含所有列的值)。
-
数量 :一张表只能有一个聚簇索引。
-
如何选择 :通常就是主键 。如果表没有主键,InnoDB 会找一个唯一非空索引替代;如果都没有,InnoDB 会隐式生成一个 6 字节的
row_id作为聚簇索引。 -
特征 :因为数据行物理上按主键顺序排列,所以主键推荐使用自增 ID,这样插入是顺序追加,不会导致 B+ 树频繁的页分裂(Page Split)。
非聚簇索引(Secondary Index / 二级索引)
-
概念:除了主键索引外,我们建的其他所有索引(唯一索引、普通索引、联合索引)都是非聚簇索引。
-
叶子节点 :存放的是索引列的值 + 主键值(注意,这里不存完整行数据)。
-
数量:一张表可以有多个。
-
回表(Bookmark Lookup) :当我们通过二级索引查到主键 ID 后,还需要拿着这个 ID 回到聚簇索引的 B+ 树中再查一次,才能拿到完整行数据。这个过程叫做"回表"。
-
覆盖索引(Covering Index):如果我们查询的列,正好全都在二级索引的叶子节点上(比如只查主键和索引列),就无需回表,这就叫覆盖索引,性能极高。