要将二元查找树(BST)转换为排序的双向链表且不创建新节点,核心思路是利用 BST 的中序遍历特性

要将二元查找树(BST)转换为排序的双向链表且不创建新节点,核心思路是利用 BST 的中序遍历特性(中序遍历结果为递增序列),在遍历过程中调整节点的指针指向。以下是具体步骤和实现逻辑:

一、核心思路

  1. 中序遍历 BST:BST 的中序遍历结果是 升序序列,这与目标双向链表的顺序一致。
  2. 调整指针关系
    • 遍历过程中,记录当前节点的 前驱节点(中序遍历的前一个节点)。
    • 将前驱节点的 right 指针指向当前节点(作为双向链表的 next)。
    • 将当前节点的 left 指针指向前驱节点(作为双向链表的 prev)。
  3. 递归实现:通过递归中序遍历,自底向上调整指针,避免使用额外空间(除递归栈外)。

二、步骤分解

1. 初始化前驱节点
  • 使用一个全局变量(或引用传递的指针)记录中序遍历的前驱节点,初始化为 null
2. 中序遍历并调整指针
  • 递归左子树:先处理左子树,确保左子树已转换为双向链表。
  • 处理当前节点
    • 若前驱节点不为 null,则将前驱节点的 right 指向当前节点。
    • 将当前节点的 left 指向前驱节点。
    • 更新前驱节点为当前节点。
  • 递归右子树:处理右子树,继续构建双向链表。
3. 找到链表头节点
  • 中序遍历的第一个节点(BST 最左节点)即为双向链表的头节点。

三、代码实现(Java 示例)

java


public class Solution {
private TreeNode prev = null; // 记录前驱节点
private TreeNode head = null; // 链表头节点
public TreeNode convert(TreeNode root) {
if (root == null) return null;
// 中序遍历左子树
convert(root.left);
// 处理当前节点
if (prev == null) {
head = root; // 第一个节点作为头节点
} else {
prev.right = root; // 前驱节点的右指针指向当前节点
root.left = prev; // 当前节点的左指针指向前驱节点
}
prev = root; // 更新前驱节点
// 中序遍历右子树
convert(root.right);
return head;
}
}
// 二叉树节点定义
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}

四、关键点说明

  • 指针调整逻辑:通过 prev 记录上一个节点,实现双向链表的 prevnext 关系(即原树的 leftright 指针复用)。
  • 头节点确定:中序遍历的第一个节点(最左叶子节点)是链表的头节点,此时 prevnull,故将 head 设为当前节点。
  • 时间复杂度:O(n),其中 n 为节点数,每个节点仅遍历一次。
  • 空间复杂度:O(h),h 为树的高度,递归栈深度取决于树的高度(平衡树时 h=log n,最坏情况 h=n)。

五、示例演示

以如下 BST 为例:


4
/ \
2 5
/ \
1 3

中序遍历顺序:1 → 2 → 3 → 4 → 5
转换后双向链表
1 <-> 2 <-> 3 <-> 4 <-> 5(其中 <-> 表示双向指针)

总结

通过中序遍历 BST,在遍历过程中调整节点的 leftright 指针,即可将 BST 原地转换为排序的双向链表。核心是利用 BST 中序遍历的有序性,以及递归实现时对前驱节点的跟踪。

在 Python 中实现二元查找树(BST)到排序双向链表的转换,核心思路与 Java 版本一致,即利用中序遍历的有序性,通过递归调整节点指针。以下是具体实现代码:

Python 实现代码

python


class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left # 原左子树指针,转换后作为链表的 prev
self.right = right # 原右子树指针,转换后作为链表的 next
class Solution:
def __init__(self):
self.prev = None # 记录中序遍历的前驱节点
self.head = None # 双向链表的头节点
def convert_bst_to_doubly_linked_list(self, root):
if not root:
return None
# 中序遍历左子树
self.convert_bst_to_doubly_linked_list(root.left)
# 处理当前节点:连接前驱节点与当前节点
if not self.prev:
self.head = root # 第一个节点作为头节点
else:
self.prev.right = root # 前驱节点的 right 指向当前节点(next)
root.left = self.prev # 当前节点的 left 指向前驱节点(prev)
self.prev = root # 更新前驱节点为当前节点
# 中序遍历右子树
self.convert_bst_to_doubly_linked_list(root.right)
return self.head
# 测试示例
def print_doubly_linked_list(head):
"""打印双向链表(正序和逆序验证)"""
print("正序遍历: ", end="")
current = head
while current:
print(current.val, end=" <-> ")
last = current # 记录尾节点用于逆序遍历
current = current.right
print("None")
print("逆序遍历: ", end="")
current = last
while current:
print(current.val, end=" <-> ")
current = current.left
print("None")
# 构建测试 BST
root = TreeNode(4)
root.left = TreeNode(2, TreeNode(1), TreeNode(3))
root.right = TreeNode(5)
# 转换并打印结果
solution = Solution()
dll_head = solution.convert_bst_to_doubly_linked_list(root)
print_doubly_linked_list(dll_head)

代码说明

  1. TreeNode 类:定义二叉树节点,包含 val(值)、left(左子树指针)、right(右子树指针)。转换后,left 作为双向链表的 prevright 作为 next

  2. Solution 类

    • prev:跟踪中序遍历的前驱节点,初始为 None
    • head:记录双向链表的头节点(中序遍历的第一个节点)。
    • convert_bst_to_doubly_linked_list 方法:递归中序遍历 BST,调整指针关系。
  3. 核心逻辑

    • 中序遍历左子树:确保左子树先转换为双向链表。
    • 处理当前节点
      • prevNone,说明当前节点是中序遍历的第一个节点(BST 最左节点),设为链表头节点 head
      • 否则,将 prev.right 指向当前节点,当前节点 left 指向 prev,完成双向连接。
    • 更新前驱节点:将 prev 设为当前节点,继续遍历右子树。
  4. 测试与验证print_doubly_linked_list 函数通过正序(从 head 开始)和逆序(从尾节点开始)遍历,验证链表的双向性。

输出结果


正序遍历: 1 <-> 2 <-> 3 <-> 4 <-> 5 <-> None
逆序遍历: 5 <-> 4 <-> 3 <-> 2 <-> 1 <-> None

结果符合 BST 中序遍历的升序特性,且双向指针关系正确。

复杂度分析

  • 时间复杂度:O(n),每个节点仅遍历一次(中序遍历)。
  • 空间复杂度:O(h),h 为树的高度(递归栈深度),平衡树时 h=log n,最坏情况(斜树)h=n。

关键区别(与 Java 版本对比)

  • Python 中没有显式的指针,但通过对象引用(self.prevroot.left 等)实现节点间的连接。
  • 递归逻辑完全一致,核心是利用中序遍历的有序性和前驱节点跟踪。

通过以上代码,可在不创建新节点的情况下,将 BST 原地转换为排序的双向链表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bol5261

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值