深入理解二叉树扁平化:原地转换为双向链表结构


深入理解二叉树扁平化:原地转换为双向链表结构

本文详细阐述如何将二叉树原地扁平化为一种类似双向链表的结构,其中节点的左右指针分别模拟链表的prev和next指针。通过递归辅助函数,文章深入解析了在遍历过程中如何正确地调整节点指针,以实现左-中-右的顺序连接,并避免常见的循环引用问题。此教程旨在提供一个清晰、高效的二叉树扁平化解决方案。

二叉树扁平化概述

二叉树扁平化是将一个标准的二叉树结构转换为一个类似双向链表的结构。在这个新的结构中,原二叉树的节点仍然存在,但它们的 left 和 right 指针被重新定义,分别扮演了双向链表中的 prev 和 next 角色。扁平化后的节点应遵循原二叉树的左-中-右(in-order)遍历顺序。此过程必须是“原地”(in-place)操作,即直接修改现有节点的指针,而不是创建新的节点或复制数据。最终,函数需要返回扁平化结构的最左侧节点(即原二叉树中in-order遍历的第一个节点)。

节点结构定义

首先,我们定义二叉树的节点结构,它包含一个值、一个左子节点和一个右子节点。

class BinaryTree:
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

扁平化核心思想:递归与指针重定向

实现原地扁平化的关键在于使用递归辅助函数,它能够处理当前节点及其左右子树的扁平化,并将它们正确地连接起来。辅助函数通常需要返回扁平化子树的最左侧节点和最右侧节点,以便父节点能够将它们连接到自身。

初始尝试与常见陷阱

在尝试实现时,一个常见的误区是对辅助函数返回值的初始化。例如,考虑以下初始化方式:

# 假设这是某个辅助函数内部
leftmostofleft = node
rightmostofright = node
leftmostofright = node # 潜在问题
rightmostofleft = node # 潜在问题

这种初始化方式在某些情况下会导致错误。具体来说,如果一个节点没有右子节点,而我们将其 leftmostofright 初始化为 node,那么当执行 node.right = leftmostofright 时,就会导致 node.right = node,从而形成一个循环引用。这显然不是我们期望的双向链表结构,因为链表不应该有节点指向自身作为其下一个节点。因此,在没有实际子树的情况下,对应的“最左侧”或“最右侧”节点指针应该保持为 None,直到它们被实际的子树扁平化结果填充。

优化后的递归辅助函数

为了避免上述问题并简化逻辑,我们可以采用一个更精炼的递归策略。核心思想是:

Krikey AI Krikey AI

Krikey AI 113 查看详情 Krikey AI
  1. 递归处理左子树:扁平化左子树,获取其扁平化后的最左侧和最右侧节点。
  2. 连接当前节点与左子树:将当前节点的 left 指针指向左子树扁平化后的最右侧节点,并建立从该最右侧节点到当前节点的反向连接。
  3. 递归处理右子树:扁平化右子树,获取其扁平化后的最左侧和最右侧节点。
  4. 连接当前节点与右子树:将当前节点的 right 指针指向右子树扁平化后的最左侧节点,并建立从该最左侧节点到当前节点的反向连接。
  5. 返回扁平化子树的整体最左侧和最右侧节点

以下是优化后的 helper 函数实现:

def flattenBinaryTree(root):
    # 如果根节点为空,则直接返回None
    if root is None:
        return None
    # 调用辅助函数进行扁平化,并返回扁平化结构的最左侧节点
    leftmost, _ = helper(root)
    return leftmost

def helper(node):
    # 递归终止条件:如果节点为空,则返回None, None
    if node is None:
        return None, None

    # 初始化当前节点为扁平化结构的最左和最右节点,
    # 假设它目前是独立的,没有左右子树连接
    leftmost_of_flattened_subtree = node
    rightmost_of_flattened_subtree = node

    # 1. 扁平化左子树
    # 如果存在左子节点
    if node.left:
        # 递归调用helper,获取左子树扁平化后的最左侧和最右侧节点
        # 注意:这里将返回的最左侧节点赋给 leftmost_of_flattened_subtree,
        # 因为整个扁平化结构的最左侧节点将来自最左侧的子树。
        # 同时,将左子树扁平化后的最右侧节点赋给 node.left,
        # 这样 node.left 现在指向了它在双向链表中的“前一个”节点。
        leftmost_of_flattened_subtree, node.left = helper(node.left)

        # 建立从左子树扁平化后的最右侧节点到当前节点的连接
        # 即:前一个节点的 right 指针指向当前节点
        node.left.right = node

    # 2. 扁平化右子树
    # 如果存在右子节点
    if node.right:
        # 递归调用helper,获取右子树扁平化后的最左侧和最右侧节点
        # 注意:这里将右子树扁平化后的最左侧节点赋给 node.right,
        # 这样 node.right 现在指向了它在双向链表中的“后一个”节点。
        # 同时,将返回的最右侧节点赋给 rightmost_of_flattened_subtree,
        # 因为整个扁平化结构的最右侧节点将来自最右侧的子树。
        node.right, rightmost_of_flattened_subtree = helper(node.right)

        # 建立从右子树扁平化后的最左侧节点到当前节点的反向连接
        # 即:后一个节点的 left 指针指向当前节点
        node.right.left = node

    # 返回当前节点所代表的扁平化子树的最左侧和最右侧节点
    return leftmost_of_flattened_subtree, rightmost_of_flattened_subtree

代码解析与注意事项

  1. flattenBinaryTree(root) 函数:这是对外暴露的接口。它首先处理根节点为空的情况,然后调用 helper 函数。helper 函数返回的是扁平化结构的最左侧节点和最右侧节点,但 flattenBinaryTree 只需要返回最左侧节点,因为它是链表的起点。
  2. helper(node) 函数
    • 基线条件:if node is None: return None, None。当遇到空节点时,没有可扁平化的内容,返回 None。
    • 初始化 leftmost_of_flattened_subtree 和 rightmost_of_flattened_subtree
      • 它们最初都指向 node 本身。这意味着如果 node 是一个叶子节点,那么它就是自身扁平化后的最左和最右节点。
      • 如果 node 有左子树,leftmost_of_flattened_subtree 会被更新为左子树扁平化后的最左节点。
      • 如果 node 有右子树,rightmost_of_flattened_subtree 会被更新为右子树扁平化后的最右节点。
    • 处理左子树 (if node.left:)
      • leftmost_of_flattened_subtree, node.left = helper(node.left):
        • 递归地扁平化左子树。
        • leftmost_of_flattened_subtree 被更新为左子树扁平化后的最左节点。这是因为整个扁平化序列的起点必然在当前节点的左子树中(如果存在)。
        • node.left 被赋值为 helper(node.left) 返回的第二个值,即左子树扁平化后的最右节点。这意味着 node.left 现在指向了它在扁平化链表中的“前一个”节点。
      • node.left.right = node:建立从左子树扁平化后的最右节点到当前节点的 right 指针连接。
    • 处理右子树 (if node.right:)
      • node.right, rightmost_of_flattened_subtree = helper(node.right):
        • 递归地扁平化右子树。
        • node.right 被赋值为 helper(node.right) 返回的第一个值,即右子树扁平化后的最左节点。这意味着 node.right 现在指向了它在扁平化链表中的“后一个”节点。
        • rightmost_of_flattened_subtree 被更新为右子树扁平化后的最右节点。这是因为整个扁平化序列的终点必然在当前节点的右子树中(如果存在)。
      • node.right.left = node:建立从右子树扁平化后的最左节点到当前节点的 left 指针连接。
    • 返回:return leftmost_of_flattened_subtree, rightmost_of_flattened_subtree。返回当前节点所代表的扁平化子树的整体最左侧和最右侧节点,供上一层递归调用使用。

这种方法巧妙地利用了Python的多重赋值特性,直接将递归调用返回的最左/最右节点赋值给对应的变量和当前节点的 left/right 指针,从而避免了中间变量的复杂性,并确保了指针的正确连接。

示例与测试

为了验证扁平化过程,我们需要一个辅助函数来插入节点和遍历扁平化后的链表。

# 假设 BinaryTree 类定义如上

# 扁平化主函数和辅助函数定义如上

# 用于测试的 BinaryTree 辅助方法
class BinaryTree(BinaryTree): # 继承以添加辅助方法
    def insert(self, values, i=0):
        if i >= len(values):
            return
        queue = [self]
        while len(queue) > 0:
            current = queue.pop(0)
            if current.left is None:
                current.left = BinaryTree(values[i])
                break
            queue.append(current.left)
            if current.right is None:
                current.right = BinaryTree(values[i])
                break
            queue.append(current.right)
        self.insert(values, i + 1)
        return self

    def leftToRightToLeft(self):
        nodes = []
        current = self
        # 从左到右遍历
        while current.right is not None:
            nodes.append(current.value)
            current = current.right
        nodes.append(current.value) # 添加最右侧节点

        # 从右到左遍历
        while current is not None:
            nodes.append(current.value)
            current = current.left
        return nodes

# 测试用例
if __name__ == "__main__":
    # 构建一个示例二叉树
    #       1
    #      / \
    #     2   3
    #    / \   \
    #   4   5   6
    #      / \
    #     7   8
    root = BinaryTree(1).insert([2, 3, 4, 5, 6])
    root.left.right.left = BinaryTree(7)
    root.left.right.right = BinaryTree(8)

    print("原始树结构(in-order 遍历):")
    # 模拟in-order遍历
    def in_order_tr*ersal(node, result):
        if node is None:
            return
        in_order_tr*ersal(node.left, result)
        result.append(node.value)
        in_order_tr*ersal(node.right, result)

    in_order_result = []
    in_order_tr*ersal(root, in_order_result)
    print(in_order_result) # 期望扁平化后的顺序:[4, 2, 7, 5, 8, 1, 6, 3]

    # 扁平化二叉树
    leftMostNode = flattenBinaryTree(root)

    # 验证扁平化后的链表
    flattened_values = leftMostNode.leftToRightToLeft()
    print("扁平化后从左到右再到左遍历结果:")
    print(flattened_values)

    # 期望结果 (leftToRightToLeft): [4, 2, 7, 5, 8, 1, 6, 3, 3, 6, 1, 8, 5, 7, 2, 4]
    expected = [4, 2, 7, 5, 8, 1, 6, 3, 3, 6, 1, 8, 5, 7, 2, 4]
    assert flattened_values == expected
    print("测试通过!")

总结

二叉树原地扁平化为双向链表结构是一个经典的递归问题,它要求我们深入理解二叉树的遍历顺序以及如何精确地操纵节点指针。通过一个返回子树最左和最右节点的辅助函数,我们可以高效地在in-order遍历过程中建立起前后节点间的双向连接。关键在于正确处理递归返回值的赋值,确保 node.left 和 node.right 最终指向其在扁平化链表中的 prev 和 next 节点,同时避免循环引用。这种方法不仅实现了原地操作,而且逻辑清晰,易于理解和维护。

以上就是深入理解二叉树扁平化:原地转换为双向链表结构的详细内容,更多请关注其它相关文章!


# 是一个  # seo后期优化方案  # 营销型网站建设如何开展  # 关键词平台排名  # 推广门店营销  # 河间seo优化选哪家  # 专注网站推广联系人  # 重庆seo工作  # 织金营销抖音推广方案  # 深圳外贸独立网站推广  # 龙华seo哪家最好  # 这是  # python  # 转换为  # 它在  # 二叉树  # 链表  # 遍历  # 递归  # 扁平化  # 子树  # ai  # app  # node 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 《书耽》更换手机号方法  毒蘑菇VOLUMESHADER_BM官网首页登录入口 毒蘑菇VOLUMESHADER_BM官网首页登录入口说明  圆通快递官方入口不需要登录 在线查询入口快速查询  PHP动态导航按钮:根据用户登录状态切换链接与文本  发布小红书怎么屏蔽粉丝?屏蔽粉丝能看到吗?  顺丰快递单号查询寄件人 顺丰寄件人查询入口  怎样让Windows 11的开始菜单恢复经典样式_Open-Shell工具使用指南【怀旧】  Sublime怎么配置YAML文件格式化_Sublime YAML Formatter插件教程  苹果17 Pro如何启用分屏浏览_iPhone 17 Pro分屏浏览设置步骤  123网页端官方登录页 123邮箱网页版即时通讯服务  《咸鱼之王》新版孙坚技能解析  《火影忍者:木叶高手》快速升级攻略  J*aScript深度克隆:实现高效、健壮与安全的复杂对象复制  QQ邮箱PC端登录页面_QQ邮箱网页版登录界面  抖音作品被限流怎么办 抖音内容优化与流量恢复方法  《深林》冬季章节图文攻略  《健康大兴》注册方法介绍  Python实时数据流中高效查找最大最小值  使用TinyButStrong生成HTML并结合Dompdf创建PDF教程  《领英》查看屏蔽名单方法  苹果手机怎么合并照片_苹果手机合并多张照片的操作方法  QQ网站入口直接登录 QQ官方正版登录页面  构建可配置的J*aScript加权点击计数器与共享总计功能  京东快递包裹信息查询入口 京东快递官方查询平台入口  银信通自动开通原因揭秘  C#解析并修改XML后保存 如何确保格式与编码的正确性  抖音火山版注销账号抖音会注销吗 抖音火山版与抖音账号注销关系  在J*a中如何实现在线问答与评分系统_问答评分项目开发方法说明  OpenWeatherMap API:通过城市名称获取天气预报数据指南  小米civi如何设置锁屏时间  顺丰快递在线查询系统 顺丰快递官方查单入口  c++如何链接Boost库_c++准标准库的集成与使用  优化Leaflet弹出层图片显示:条件渲染策略  使用AI在VS Code中将代码从一种语言翻译成另一种  CSS动画如何实现图标旋转并放大_transform rotate scale @keyframes实现  国际经济与贸易就业方向解析  Win10锁屏时间怎么设置 Win10调整自动锁屏时间方法  OPPO A3 WiFi频繁断开怎么办 OPPO A3网络优化技巧  CSS如何使用outline-offset与颜色组合突出元素边框  斯宾塞称XGP云游戏“蒸蒸日上”:正在构建一个游戏从未如此唾手可得的未来  Excel怎么用XLOOKUP函数实现双向查找_ExcelXLOOKUP替代VLOOKUP+HLOOKUP的高级用法  如何在CSS中使用过渡制作按钮边框渐变_border-color transition实现  汽水音乐在线听歌网页版 汽水音乐在线听歌网页版入口  百度网盘如何设置上传限额  电脑开不了机怎么办 电脑无法开机的解决方法  《百度畅听版》关闭兴趣推荐方法  《飞猪旅行》购买汽车票方法  PDF如何批量加注释_PDF多文件批注高亮操作教程  多闪电脑版下载_多闪PC端模拟器使用  CSS过渡与滚动滚动事件结合应用_scroll与transition动画 

 2025-12-04

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.