一、链表核心进阶算法
链表是线性表的核心,相较于数组,链表支持动态扩容、灵活插入删除。进阶面试重点考察:链表成环检测、入环点定位、原地反转。
1.1 成环链表与快慢指针策略
1.1.1 成环检测核心逻辑
采用经典**快慢指针(双指针)**算法,空间复杂度 O(1),无需额外集合存储节点,是最优解法。
-
指针规则:慢指针每次走 1 步,快指针每次走 2 步。
-
判环逻辑 :若链表存在环,快慢指针最终必然在环内相遇;若链表无环,快指针会优先遍历到链表末尾(
next = null)。
1.1.2 入环节点精准定位
判断有环后,需要精准找到环的入口节点,算法有严格数学推导支撑。
-
操作步骤 :快慢指针环内相遇后,将任意一个指针重置至链表头节点,之后两个指针同步每次走 1 步,再次相遇的节点即为环入口节点。
-
数学推导 :设
X= 头节点到入口距离,Y= 入口到相遇点距离,Z= 相遇点回到入口距离。根据快慢指针路程关系可推导出:X = n*(Y+Z) + Z,以此证明同步遍历必相遇于入口。
1.2 链表原地反转实现
1.2.1 双指针反转核心思想
采用 pre + index 双游标 实现链表原地反转,不创建新链表,仅修改原节点指针指向,空间复杂度 O(1)。
-
pre:前驱节点,初始值为 null
-
index:当前遍历节点,初始为头节点
-
临时变量:记录下一个节点地址,防止断链
1.2.2 执行流程
-
临时变量保存当前节点的下一个节点地址;
-
修改当前节点 next 指向,指向前驱节点 pre;
-
pre、index 整体后移,循环遍历至链表末尾。
1.2.3 内存管理核心注意点
核心避坑点:修改节点 next 域之前,必须先用变量持有后续节点地址。若直接修改指针,会丢失后续所有节点引用,造成节点丢失、内存泄漏。
二、有序二叉树(BST)构建与遍历
有序二叉树(二叉搜索树 BST)核心特性:左子树所有节点值 < 根节点值 < 右子树所有节点值。基于该特性,BST 拥有高效的查找、插入、删除性能。
2.1 二叉树物理结构与手动构建
2.1.1 节点存储模型
BST 最小单元为树节点,每个节点包含三个核心域,结合 Java 内存机制:
-
value:存储节点数据;
-
left:左子节点引用地址;
-
right:右子节点引用地址。
内存分配:节点对象在堆内存分配空间,栈内存的引用变量仅存储对象地址。
2.1.2 手动构建流程
通过 new Node() 创建零散独立节点,手动赋值 left、right 指针,严格遵循 左小右大 规则拼接为完整二叉树,适合理解底层结构。
2.2 自动构建与插入方法
手动构建效率极低,实际开发封装 insert 方法实现节点自动插入、自动构建 BST。
2.2.1 插入核心逻辑
-
若根节点为空,新节点直接作为根节点;
-
定义游标从根节点开始遍历,比较节点值大小;
-
小于当前节点向左遍历,大于等于当前节点向右遍历;
-
遍历到空位置时挂载新节点,始终维护 BST 有序性。
2.2.2 this 关键字作用
构造方法中 this.value 用于区分成员变量 与局部形参,解决变量命名冲突;无冲突场景下 this 可省略。
2.3 二叉树两大核心遍历方式
2.3.1 广度优先遍历(BFS 层序遍历)
基于 Queue 队列 实现,从上到下、从左到右逐层遍历节点。
流程:根节点入队 → 循环出队访问节点 → 非空左右子节点依次入队 → 队列为空遍历结束。
2.3.2 深度优先遍历(DFS 递归遍历)
基于递归栈帧实现,分为三种遍历规则,可通过栈帧图完整分析递归执行流程:
-
前序遍历:根 → 左 → 右
-
中序遍历:左 → 根 → 右(BST 中序遍历结果为升序有序序列)
-
后序遍历:左 → 右 → 根
三、有序二叉树(BST)删除操作详解
BST 删除是二叉树核心难点,需根据目标节点的子树情况分三种场景处理,核心原则:删除后不破坏 BST 左小右大的有序特性。
3.1 场景一:删除叶子节点(无左右子树)
-
普通叶子节点:将父节点对应的 left/right 指针置空;
-
特殊根节点:若整棵树仅有一个根节点,直接将 root 置为 null。
3.2 场景二:删除仅有一棵子树的节点
采用子树提升策略,让子树直接替代被删除节点的位置。
-
普通节点:父节点指针跳过目标节点,直接指向其唯一的左/右子树;
-
根节点:直接将根节点指向其唯一子树。
3.3 场景三:删除拥有两棵子树的节点
无法直接删除,采用 值替换法 保证树结构有序:
-
找到目标节点右子树的最小值节点(或左子树最大值节点);
-
用最值节点的值覆盖待删除节点的值;
-
删除原最值节点(该节点必为叶子/单子树节点,可复用前两种删除逻辑)。
3.4 核心辅助方法
为支撑增删逻辑,封装三大底层方法:
-
目标节点查找:从根节点根据值大小左右遍历,精准定位节点;
-
父节点查找:遍历过程记录父节点,用于指针修改;
-
子树最值查找:最小值一直左遍历,最大值一直右遍历。
四、完整可运行 Java 源码
4.1 节点实体类 Node.java
java
package com.qcby.有序二叉树;
public class Node {
int value;
Nt;
right;
e(int value) {
this.value = value;
Override
public String toSt{
"Node [value=" + value + ", left=" + left + ", right=" + right + "]";
}
}
return ring() }
@ public Nod Node ode lef
4.2 二叉树核心功能类 BinaryTree.java
java
package com.qcby.有序二叉树;
import java.util.LinkedList;
import java.util.Queue;
public class BinaryTree {
Node root = null;
// 二叉树节点插入、自动构建BST
public void insert(int value) {
Node node = new Node(value);
if(root==null) {
root=node;
return;
}
Node index = root;
while(true) {
if(node.value < index.value) {
if(index.left==null) {
index.left=node;
return;
}
index=index.left;
}else {
if(index.right==null) {
index.right=node;
return;
}
index=index.right;
}
}
}
// 广度优先遍历(层序遍历)
public void levelOrder() {
Queue<Node> queue = new LinkedList<Node>();
if(root!=null) {
queue.add(root);
}
while(!queue.isEmpty()) {
Node temp = queue.poll();
System.out.print(temp.value + " ");
if(temp.left!=null) {
queue.add(temp.left);
}
if(temp.right!=null) {
queue.add(temp.right);
}
}
System.out.println();
}
// 深度优先-先序遍历
public void beforeOrder(Node node) {
if(node==null) {
return;
}
System.out.print(node.value + " ");
beforeOrder(node.left);
beforeOrder(node.right);
}
// 深度优先-中序遍历
public void inOrder(Node node) {
if(node==null) {
return;
}
inOrder(node.left);
System.out.print(node.value + " ");
inOrder(node.right);
}
// 深度优先-后序遍历
public void afterOrder(Node node) {
if(node==null) {
return;
}
afterOrder(node.left);
afterOrder(node.right);
System.out.print(node.value + " ");
}
// 查找目标节点
public Node find(int value) {
Node index = root;
while(index!=null) {
if(index.value==value) {
return index;
}else if(index.value>value) {
index=index.left;
}else {
index=index.right;
}
}
return null;
}
// 查找目标节点的父节点
public Node findParentNode(int value) {
Node index = root;
while (index!=null) {
if((index.left!=null&&index.left.value==value)||(index.right!=null&&index.right.value==value)) {
return index;
}else if(index.value>value) {
index=index.left;
}else {
index=index.right;
}
}
return null;
}
// 查找子树最小值
public int rightMin(Node node) {
Node index = node;
while(index.left!=null) {
index=index.left;
}
return index.value;
}
// BST节点删除核心方法
public void delete(int value) {
Node target = find(value);
if(target==null) {
System.out.println("没有这个节点");
return;
}
Node parent = findParentNode(value);
// 1. 删除叶子节点
if(target.left==null&&target.right==null) {
if(parent==null) {
root = null;
return;
}
if(parent.left==target) {
parent.left = null;
}else {
parent.right=null;
}
}
// 2. 删除拥有两棵子树的节点
else if(target.left!=null&&target.right!=null) {
int min = rightMin(target.right);
delete(min);
target.value=min;
}
// 3. 删除仅有一棵子树的节点
else {
if(parent==null) {
if(target.left!=null) {
root = root.left;
}else {
root = root.right;
}
return;
}
if(parent.left==target) {
if(target.left!=null) {
parent.left=target.left;
}else {
parent.left=target.right;
}
}else {
if(target.left!=null) {
parent.right=target.left;
}else {
parent.right=target.right;
}
}
}
}
}
4.3 测试类 Test.java
java
package com.qcby.有序二叉树;
public class Test {
public static void main(String[] args) {
BinaryTree tree = new BinaryTree();
// 插入节点构建BST
tree.insert(5);
tree.insert(7);
tree.insert(4);
tree.insert(2);
tree.insert(6);
tree.insert(10);
System.out.println("中序遍历结果(有序):");
tree.inOrder(tree.root);
}
}
五、核心知识点总结
-
链表进阶:快慢指针高效解决链表成环检测与入环点定位,双指针原地反转实现无额外空间翻转,核心避坑点为防止断链内存泄漏;
-
BST 核心特性:左小右大的有序规则是所有增、删、查、遍历逻辑的基础;
-
遍历方式:BFS 队列实现层序遍历,DFS 递归实现三序遍历,中序遍历可输出有序数组;
-
BST 删除重难点:分叶子节点、单子树、双子树三种场景,双子树节点采用「右子树最小值替换法」。