【C语言】打印二叉树树形(制表符实现,清晰+高拓展)(2022-10-22 更新—偏移量说明)

本文详细解释了在C语言中使用制表符打印二叉树时,如何通过偏移量实现清晰的树形布局。作者补充了关于偏移参数tap、tap1、tap2和tap3的作用,并提供了效果展示和核心代码解读,强调了确定根节点位置的重要性。此外,文章还讨论了拓展性和代码优化。


2022-10-22 补充

好久没来 优快云 了,今晚一看发现一些小伙伴反应说看文中的 偏移量 看的很晕(当时自己写比较主观哈哈),秉着要对文章负责的态度,我在这里统一给大家详细解释一下
如果是第一次来的话,可以先去看正文
没想到这么久了还有人看我的文章,挺感动的,先谢谢大家的支持了~

废话不多说,先看画的这张图(相信大家看到图心里应该就有底了)
这张图里边已经基本上把算法的核心全部画出来了,确认根结点的位置是重点(画树枝是次要的)
说明:为了画线比较好看,我把对 x 坐标的减 1 操作放到了第一个偏移量的位置,这样能使得线段对的上每个结点
在这里插入图片描述

首先要解释的就是 偏移参数 以及 三个偏移量 的作用:

  1. tap:这个是父节点传递给子结点的相对偏移量
    如算法中所示,它是直接针对 tap1 的,用于计算父树的偏移量
  2. tap1:也就是图中红色的划线,该偏移我叫做 父树偏移量
    是为了计算当前结点的父节点再整个空间的偏移量,加上这个偏移量,父节点的位置就相当于初始化状态的父节点,它的子结点(当前结点)可以直接基于父节点来计算自己的偏移量
  3. tap2:也就是图中绿色的划线,该偏移我叫做 子树偏移量
    这是在子树为右子树的情况下需要的,前边的 tap1 我们已经确定了以父节点为根的树在空间中的位置,而子结点如果是右子树的话,那么还需要将左子树的位置空出来,tap2 就是干这件事的
  4. tap3:也就是图中橙色的画线,该偏移我叫做 半偏移量
    这是左右子树都需要的,左子节点用 tap1 就可以确定相对位置,而右子节点用 tap1 和 tap2 的和,也就确定了和左子树一样的相对位置(分别使用者两个偏移量,左右子树的状态就相同了,tap3 的规则对他们来说是一样的)
    准确地说,tap3 是树枝的长度,也就是为了下面的树能有空间继续放的下去而需要的偏移量

到这里,大家对几个偏移量的作用应该基本上都清楚了,至于每个偏移量的计算方法,这里我简单说明一下(手动在纸上模拟一遍更好理解哦),这里我顺序倒着解释可能会更好

  1. tap3:首先是 tap3,它的作用就是为下边的树空出位置,计算方法是 2treeDepth-depth-1
    什么意思呢?以上图为例,该树深度为 4,则宽度为 24=16,所以一个子树的宽度应该是 8,也就是24-1
    所以上式的 -1 操作表明了,子树的宽度是父树的一般。再而,每往下一层,子树的宽度都会减半,所以得减去当前深度 depth,所以最终的指数部分就是 treeDepth-depth-1
  2. tap2:然后是 tap2,它的作用是把右子树向右挪出一个左子树的位置
    所以我们需要的,自然就是左子树的宽度啦,由于是在同一层,所以不需要减半也就不用 -1,所以指数部分是 treeDepth-depth,而前边的 right 就好理解了,因为右子树才需要否则置为 0 表示不需要
  3. tap1:再而是 tap1,它的作用是确定父树在整个空间的偏移量
    而该父树的父树可能是左子树也可能是右子树,爷爷也可能是左子树和右子树,这么多情况要怎么确定呢?
    答案就是 tap 偏移量了,由它来记录前边的爸爸爷爷们究竟有多少是右子树,得到一个累计值,然后 tap1 就可以根据这个累计值算出整体在空间的偏移量了!由于是在同一层,所以也不需要 -1
  4. tap:这个在 tap1 已经介绍过了,就是相对偏移量,用来保存该节点的爸爸爷爷到底做了多少次右子树,是一个累计值

至于你们要问,为什么我会知道需要这些偏移量。我只能说,这是一点点调试出来的(无奈)
一开始就是只有 tap3,确定需要为子树空出多少就好了嘛
然后就发现,怎么区分左右子树?!所以就出现了 tap2
最后才发现最致命的一点,一层层的递归下,每棵小树(父节点+子结点)他们分别在空间的哪个位置?所以出现了辅助性的 tap 和用于确定相对位置的 tap1

好了,整一个完整的说明就到这里了
从看到评论和私信,到拿起一年前的代码重新理解(哭),再到捋好思路将说明补充好,已经过去一个半小时了
虽然这对我来说有点花时间(一年过去了,心态也不太一样了)
但我自己清楚在看别人博文看不懂,自己苦苦理解几个小时没出来个结果是多么痛苦(我看别人博文时,对于说的含含糊糊的也有要骂街的冲动2333),所以我也决心一定要把思路讲清楚,让自己成为自己讨厌的人,是很不应该的啊

就到这里啦,今年起来就没写过博客,今天也顺带把手感提上来了,后边看情况再更新吧
看到以前的文章还能对大家有帮助,挺开心的,能帮到你们就好。溜了溜了~(有什么问题可以再评论或者私信我哦)


0. 前言

上 优快云 找了很多份树形代码,一个共通的感觉就是拓展性不够强
比如说树大了节点之间被撑开无法兼容性不好
没有使用树枝连接的画显示稍显混乱,使用斜线当树枝又不好拓展
还有一部分就是代码太冗长看不懂

基于此,我打算用回制表符,力求清晰显示出树形
肝了几个小时的代码,过程中一点一点调出来树形
代码部分也在不断优化,思路在不断的调试中也不断改变
最后总结出一种以绝对偏移量画树形的方法,下边将一一对代码进行解释

1. 效果展示

首先树画出来的效果如下,可以很好兼容各种树形,显示也很清晰
在这里插入图片描述
在这里插入图片描述
拓展性扛扛的完全不用担心
在这里插入图片描述

2. 核心代码及解读

① 核心代码

代码也是简洁 + 清晰

/*
* 递归打印打印出树形
* T		正在打印的树
* depth	目前在打印树的第几层
* right	该子树是否为右子树
* tap	目前子树需要的相对偏移数量
*/
Status Traverse_R(BiTree T, int depth, int right, int tap) {
   
   
	if (T == NULL) return OK;

	// 获取一次树的初始高度,用于计算相对偏移数量
	static int treeDepth = BiTreeDepth(T);
	// 记录当前光标位置,用于在递归中记录当前递归时树的位置
	int x, y;
	// 拆分树,将树的左右子树拆分开来
	BiTree L, R;
	BreakBiTree(T, L, R);

	// 计算父树的偏移量
	int tap1 =
<think>好的,我现在需要帮助用户解决如何用代码实现打印二叉树结构的问题。首先,我得回想一下用户提供的引用内容,看看有没有相关的信息。用户提到了引用[2]和引用[4],其中引用[2]讨论了用括号法打印二叉树,而引用[4]展示了Python分层打印的代码。这可能是一个关键点。 用户的问题是关于打印二叉树结构,可能有不同的方法,比如括号表示法或者分层打印。我需要先确定用户具体需要哪种方式。根据引用[2],括号法是基于先序遍历,并在适当的位置添加括号和逗号来区分左右子树。而引用[4]中的例子是分层打印,输结果为12345,可能是指按层序遍历的结果。 我需要将这两种方法都解释清楚,并提供相应的代码示例。首先,处理括号法。根据先序遍历,访问根节点,然后递归处理左右子树。当左子树或右子树存在时,需要添加括号。例如,如果左子树存在但右子树不存在,左子树需要用括号括起来,而右子树不需要。这需要修改先序遍历的代码,在适当的位置插入括号和逗号。 然后是分层打印,也就是层序遍历。这通常使用队列来实现,逐层访问节点,并记录每层的节点值。引用[4]中的代码可能用了一个简单的队列实现,但可能需要更详细的处理,比如处理每层的节点数,或者在打印时换行。 接下来,我需要检查用户提供的引用内容中的代码示例,是否有可以借鉴的部分。例如,引用[4]中的代码片段构造了一个二叉树,并调用printlayer函数,输结果是一行数字,可能没有分层结构。因此,可能需要更完善的层序遍历代码,确保每层单独打印。 另外,用户提到的引用[3]提到顺序存储适用于完全二叉树和满二叉树,但用户的问题是关于链式结构的打印,所以可能需要使用链式存储结构的遍历方法,如递归或队列。 在实现括号表示法时,需要注意边界情况,比如空子树不需要括号,只有左子树或右子树存在时的处理。例如,当左子树存在而右子树为空时,左子树需要括号,而右子树不需要,反之亦然。这需要递归过程中判断子节点是否存在。 对于分层打印,可能需要使用队列来按层存储节点,并在每一层开始时记录当前层的节点数量,依次处理这些节点,并将它们的子节点加入队列。每处理完一层,就换行或进行其他格式化输。 现在,我需要编写这两种方法的代码示例,并确保它们符合用户提供的引用中的信息。例如,在括号法中,参考引用[2]中的思路,修改先序遍历代码。在分层打印中,参考引用[4]的示例,但可能需要更详细的实现。 另外,需要注意代码的注释和可读性,使用中文注释可能更适合用户的需求。同时,需要处理节点值为非单字符的情况,但用户示例中的数字可能比较简单,暂时可以假设节点值为单字符或数字。 最后,生成相关问题,帮助用户进一步了解二叉树的其他操作或相关算法。例如,如何实现二叉树的遍历,或者不同打印方式的优缺点。 总结步骤: 1. 介绍括号表示法,基于先序遍历,添加括号和逗号。 2. 提供括号法的Python代码示例。 3. 介绍分层打印法,使用层序遍历和队列。 4. 提供分层打印的Python代码示例。 5. 生成相关问题,如遍历方法、优化等。</think>以下是两种常见的二叉树结构打印实现方法及Python代码示例: ### 方法一:括号表示法(基于先序遍历) 基于引用[2]中提到的括号法原理,通过先序遍历添加括号和逗号: ```python class Node: def __init__(self, val): self.val = val self.left = None self.right = None def print_tree(root): if not root: return "" res = str(root.val) if root.left or root.right: res += "(" res += print_tree(root.left) if root.right: # 右子树存在时才加逗号 res += "," res += print_tree(root.right) res += ")" return res # 测试示例 root = Node(1) root.left = Node(2) root.right = Node(3) root.left.left = Node(4) root.left.right = Node(5) print(print_tree(root)) # 输:1(2(4,5),3) ``` ### 方法二:分层打印(基于层序遍历) 基于引用[4]的分层打印思路,使用队列实现: ```python from collections import deque class Node: def __init__(self, val): self.val = val self.left = None self.right = None def print_level_order(root): if not root: return q = deque([root]) while q: level_size = len(q) for _ in range(level_size): node = q.popleft() print(node.val, end="") if node.left: q.append(node.left) if node.right: q.append(node.right) print() # 换行表示新层开始 # 测试示例 root = Node(1) root.left = Node(2) root.right = Node(3) root.left.left = Node(4) root.left.right = Node(5) print_level_order(root) """ 输: 1 23 45 """ ``` 两种方法的对比特点: 1. 括号法能直观显示树结构,但横向展示空间有限[^2] 2. 分层打印更适合展示平衡树的层级关系[^4] 3. 括号法时间复杂度为$O(n)$,空间复杂度$O(h)$(h为树) 4. 分层打印时间复杂度为$O(n)$,空间复杂度$O(w)$(w为树宽)
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

寒冰小澈IceClean

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

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

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

打赏作者

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

抵扣说明:

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

余额充值