Problem: 面试题 17.12. BiNode
文章目录
- [1. 整体思路](#1. 整体思路)
- [2. 完整代码](#2. 完整代码)
- [3. 时空复杂度](#3. 时空复杂度)
-
-
- [时间复杂度: O ( N ) O(N) O(N)](#时间复杂度: O ( N ) O(N) O(N))
- [空间复杂度: O ( H ) O(H) O(H)](#空间复杂度: O ( H ) O(H) O(H))
-
1. 整体思路
核心逻辑
-
中序遍历 (In-order Traversal):
- 因为输入是二叉搜索树 (BST),通过中序遍历(左 -> 根 -> 右),我们可以按从小到大的顺序访问所有节点。
- 这个顺序正是我们需要构建的链表的顺序。
-
链表构建 (Pointer Manipulation):
- 使用一个哨兵节点(Dummy Head)
head来简化链表头部的处理。 - 使用一个全局变量
pre来记录中序遍历过程中上一个访问的节点。 - 在遍历到当前节点
node时:- 将上一个节点
pre的right指针指向当前节点node(建立链表连接)。 - 将当前节点
node的left指针置空(题目要求)。 - 更新
pre为当前节点,以便处理下一个节点。
- 将上一个节点
- 使用一个哨兵节点(Dummy Head)
-
递归实现:
- 标准的递归 DFS 实现中序遍历:
dfs(left)->处理当前->dfs(right)。
- 标准的递归 DFS 实现中序遍历:
2. 完整代码
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 {
// 创建一个哨兵节点 head,它的值不重要 (-1)
// 它的 right 指针将指向最终链表的头节点
TreeNode head = new TreeNode(-1);
// pre 用于记录中序遍历中的"前驱节点"
// 初始为 null,或者也可以指向 head,取决于具体实现逻辑
TreeNode pre = null;
public TreeNode convertBiNode(TreeNode root) {
// 开始中序遍历
dfs(root);
// 返回哨兵节点的右孩子,即链表的真正头节点
return head.right;
}
// 中序遍历辅助函数
private void dfs(TreeNode node) {
// 递归终止条件:节点为空
if (node == null) {
return;
}
// 1. 递归处理左子树
dfs(node.left);
// 2. 处理当前节点 (核心链表构建逻辑)
if (pre == null) {
// 如果 pre 为空,说明当前 node 是中序遍历访问的第一个节点(整棵树最小的节点)
// 它应该是链表的头,所以让 head.right 指向它
// 同时初始化 pre 指向当前节点
pre = node;
head.right = node;
} else {
// 如果 pre 不为空,说明之前已经访问过节点
// 将前驱节点 pre 的 right 指针指向当前节点 node,建立链表连接
pre.right = node;
// 更新 pre 指向当前节点,为下一次连接做准备
pre = node;
}
// 题目要求:所有节点的 left 指针必须置为 null
// 这一步非常重要,否则结果不是纯粹的单向链表,可能保留树的结构导致判题错误或循环引用
node.left = null;
// 3. 递归处理右子树
dfs(node.right);
}
}
3. 时空复杂度
假设二叉树的节点总数为 N N N。
时间复杂度: O ( N ) O(N) O(N)
- 计算依据 :
- 这是一个标准的深度优先搜索(DFS)遍历。
- 每个节点被访问且仅被访问一次。
- 在每个节点上的操作(修改指针、判断)都是 O ( 1 ) O(1) O(1) 的常数操作。
- 结论 : O ( N ) O(N) O(N)。
空间复杂度: O ( H ) O(H) O(H)
- 计算依据 :
- 代码是就地修改(In-place)树的指针,没有创建新的节点列表。
- 主要的额外空间消耗来自于递归调用栈。
- 递归深度取决于树的高度 H H H。
- 最坏情况(退化为链表): O ( N ) O(N) O(N)。
- 最好/平均情况(平衡树): O ( log N ) O(\log N) O(logN)。
- 结论 : O ( N ) O(N) O(N)(最坏情况)。