655 输出二叉树(递归)

本文介绍了一种在二维数组中打印二叉树的方法,确保输出符合特定格式要求,包括树的高度、宽度及节点布局规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 问题描述:

在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则:
行数 m 应当等于给定二叉树的高度。
列数 n 应当总是奇数。
根节点的值(以字符串格式给出)应当放在可放置的第一行正中间。根节点所在的行与列会将剩余空间划分为两部分(左下部分和右下部分)。你应该将左子树输出在左下部分,右子树输出在右下部分。左下和右下部分应当有相同的大小。即使一个子树为空而另一个非空,你不需要为空的子树输出任何东西,但仍需要为另一个子树留出足够的空间。然而,如果两个子树都为空则不需要为它们留出任何空间。
每个未使用的空间应包含一个空的字符串""。
使用相同的规则输出子树。

示例 1:

输入:
     1
    /
   2
输出:
[["", "1", ""],
 ["2", "", ""]]

示例 2:

输入:
     1
    / \
   2   3
    \
     4
输出:
[["", "", "", "1", "", "", ""],
 ["", "2", "", "", "", "3", ""],
 ["", "", "4", "", "", "", ""]]

示例 3:

输入:
      1
     / \
    2   5
   / 
  3 
 / 

输出:
[["",  "",  "", "",  "", "", "", "1", "",  "",  "",  "",  "", "", ""]
 ["",  "",  "", "2", "", "", "", "",  "",  "",  "",  "5", "", "", ""]
 ["",  "3", "", "",  "", "", "", "",  "",  "",  "",  "",  "", "", ""]
 ["4", "",  "", "",  "", "", "", "",  "",  "",  "",  "",  "", "", ""]]
注意: 二叉树的高度在范围 [1, 10] 中。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/print-binary-tree

2. 思路分析:

分析题目可以知道我们需要先声明一个二维的数组来记录答案(数组元素值为空串),我们只需要在这个数组的对应位置上修改为根节点的值即可,所以首先我们需要知道当前二叉树对应的宽度和高度,这样才可以声明对应长度和宽度的二维数组,我们可以使用一个递归方法来计算二叉树的长度和宽度,怎么样将当前的根节点的值填入到二维数组的对应位置呢?我们观察题目中给出的测试用例发现根节点的位置就是当前区间宽度的一半,所以我们可以使用一个递归方法传递当前的区间范围l,r和当前根节点对应的行数d等参数,每一次在递归的时候当前行数对应的区间中点就是当前需要修改的根节点的位置,递归修改每一个根节点的值即可。

3. 代码如下:

from typing import List


# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right


class Solution:
    # 求解整棵树的高度和宽度
    def dfs(self, root: TreeNode):
        if not root: return [0, 0]
        l = self.dfs(root.left)
        r = self.dfs(root.right)
        return [max(l[0], r[0]) + 1, max(l[1], r[1]) * 2 + 1]
    
    def pt(self, root: TreeNode, l: int, r: int, d: int, res: List[List[str]]):
        if not root: return
        # 观察规律可以发现需要修改的根节点的位置为当前行数对应的区间[l, r]的中点位置
        mid = l + r >> 1
        res[d][mid] = str(root.val)
        self.pt(root.left, l, mid - 1, d + 1, res)
        self.pt(root.right, mid + 1, r, d + 1, res)

    def printTree(self, root: TreeNode) -> List[List[str]]:
        t = self.dfs(root)
        h, w = t[0], t[1]
        res = [[""] * w for i in range(h)]
        self.pt(root, 0, w - 1, 0, res)
        return res
### 二叉树递归实现方法 #### 定义与基本概念 二叉树是一种常见的非线性数据结构,其特点是每个节点最多有两个子节点。为了操作这种结构,通常会使用递归来处理节点及其子树的关系。递归的核心在于函数调用自身来解决问题的一部分。 在C语言中,可以通过定义一个`struct`类型的节点来表示二叉树中的每一个节点[^3]。例如: ```c typedef char BTDataType; typedef struct BinaryTreeNode { BTDataType val; struct BinaryTreeNode* left; struct BinaryTreeNode* right; } BTNode; ``` 上述代码片段展示了如何通过结构体定义二叉树的节点类型。其中,`val`存储节点的数据值,而`left`和`right`分别指向该节点的左子节点和右子节点。 --- #### 查找特定节点 `'C'` 的递归过程 假设我们有一棵如下所示的二叉树: ``` A / \ B C / \ D E ``` 要查找字符 `'C'` 所对应的节点位置,可以编写一个递归函数 `FindNode` 来完成此任务。以下是其实现逻辑: ```c BTNode* FindNode(BTNode* root, BTDataType target) { if (root == NULL || root->val == target) { return root; // 如果当前节点为空或者找到目标,则返回 } // 在左子树中寻找 BTNode* result = FindNode(root->left, target); if (result != NULL && result->val == target) { return result; // 左子树找到了目标节点 } // 否则继续在右子树中寻找 return FindNode(root->right, target); } ``` 在这个例子中,当传入根节点为 `'A'` 并设置目标值为目标字符 `'C'` 时,程序将依次访问 `'B' -> 'D' -> 'E' -> 'C'` 节点并最终返回 `'C'` 对应的指针[^1]。 --- #### 判断是否为完全二叉树 判断一棵二叉树是否为完全二叉树是一个经典问题。一种有效的方法是借助队列进行层次遍历,并记录第一个不完整的节点之后是否有其他节点存在。如果发现有额外节点,则说明这不是一颗完全二叉树;反之则是完全二叉树。 下面是基于递归的思想描述这一判定流程的方式之一(注意实际编码可能更倾向于迭代形式): - 若某一层某个节点缺少孩子节点,则后续所有层都应该是叶子节点; - 使用辅助标志变量标记遇到的第一个不符合满条件的位置。 具体伪码可参见相关资料。 --- #### 非递归遍历对比分析 虽然本题主要讨论的是递归实现,但也提到了非递归版本作为补充知识点。对于先序遍历时,如果不希望依赖显式的函数堆栈机制而是手动管理内存空间的话,那么就需要引入用户自定义栈来进行模拟。这里给出了一种典型的非递归解决方案用于演示目的[^2]: ```c void PreOrder_non_recursion(BiTree T){ LinkStack S; InitStack(S); BiTree p,q; p=T; while(p!=NULL || !StackEmpty(S)){ if(p!=NULL){ Push(S,p); printf("%d ",p->data); /* 访问 */ p=p->lchild; /* 进入左子树 */ }else{ Pop(S,&q); p=q->rchild; /* 进入右子树 */ } } } ``` 这段代码实现了无需真正意义上的递归调用来完成相同功能的效果——即按照“根—>左—>右”的顺序输出各元素值。 --- ### 总结 综上所述,无论是简单地定位某一指定项还是验证复杂属性诸如完备性质等问题都可以很好地运用到递归技术上去解决它们各自领域内的挑战。同时也要注意到不同场景下选择合适的技术手段往往能够带来事半功倍的结果!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值