题目介绍
描述:
你需要采用前序遍历的方式,将一个二叉树转换成一个由括号和整数组成的字符串。
空节点则用一对空括号 "()" 表示。而且你需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。
示例 1:
输入: 二叉树: [1,2,3,4]
1
/ \\
2 3
/
4
输出: "1(2(4))(3)"
解释: 原本将是“1(2(4)())(3())”,
在你省略所有不必要的空括号对之后,
它将是“1(2(4))(3)”。
示例 2:
输入: 二叉树: [1,2,3,null,4]
1
/ \\
2 3
\\
4
输出: "1(2()(4))(3)"
解释: 和第一个示例相似,
除了我们不能省略第一个对括号来中断输入和输出之间的一对一映射关系。
解题思路:
递归算法的关键是要明确函数的「定义」是什么,然后相信这个定义,利用这个定义推导最终结果。
写树相关的算法,简单说就是,先搞清楚当前 root 节点该做什么,然后根据函数定义递归调用子节点,递归调用会让孩子节点做相同的事情。
二叉树题目的一个难点在于如何通过题目的要求思考出每一个节点需要做什么
二叉树解题策略
一 递归 二 队列 + 迭代 (层次遍历) 三 栈 + 迭代 (非递归遍历) 四 其它
三种基本的遍历方式,都可以用递归来实现。写递归算法的时候,需要注意递归退出条件以及递归操作的表达。
自己的解法实现
def tree2str6(self, root):
def construct(node):
if not node: # 如果到了空节点返回空字符即可
return ''
left_str = construct(node.left) # 默认获得左子树已经括好的内容
right_str = construct(node.right) # 默认获得右子树已经括好的内容
if left_str and not right_str: # 右子树的内容为空是不加括号
return str(node.val) + '(' + left_str + ')'
elif not left_str and not right_str: # 左右子树返回内容逗我空时不加括号
return str(node.val)
else: # 其他情况正常输出即可
return str(node.val) + '(' + left_str + ')' + '(' + right_str + ')'
return construct(root)
网上比较优秀的解法
解法一
方法一:递归 我们可以使用递归的方法得到二叉树的前序遍历。在递归时,根据题目描述,我们需要加上额外的括号,会有以下 4 种情况:
- 如果当前节点有两个孩子,那我们在递归时,需要在两个孩子的结果外都加上一层括号;
- 如果当前节点没有孩子,那我们不需要在节点后面加上任何括号;
- 如果当前节点只有左孩子,那我们在递归时,只需要在左孩子的结果外加上一层括号,而不需要给右孩子加上任何括号;
- 如果当前节点只有右孩子,那我们在递归时,需要先加上一层空的括号 () 表示左孩子为空,再对右孩子进行递归,并在结果外加上一层括号。
def tree2str(self, root):
if not root: return ""
if not root.left and not root.right:
return str(root.val) + ""
if not root.right:
return str(root.val) + "(" + self.tree2str(root.left) + ")"
return str(root.val) + "(" + self.tree2str(root.left) + ")(" + self.tree2str(root.right) + ")"
解法二
题目的意思是子节点需要用()来包裹。举例来说,二叉树[root,left,right],则转换为root(left)(right)。如果只有left为空节点,则输出root()(right);如果只有right为空节点则可以忽略右节点的(),输出为root(left)。
解题方法: 先序遍历,并判断不同情况: (1)如果当前节点为空,则返回空字符串 (2)如果左子节点非空,右子节点空,则返回str(node.val) + '(' + dfs(node.left) + ')' (3)如果左子节点空,右子节点非空,则返回str(node.val) + '()' + '(' + dfs(node.right) + ')' (4)如果左右子节点都为空,则返回str(node.val) (5)除上述情况,则左右节点都存在,返回str(node.val) + '(' + dfs(node.left) + ')' + '(' + dfs(node.right) + ')'
def tree2str3(self, root):
def dfs(node):
if not node: return ''
if node.left and not node.right:
return str(node.val) + '(' + dfs(node.left) + ')'
if not node.left and node.right:
return str(node.val) + '()' + '(' + dfs(node.right) + ')'
if not node.left and not node.right:
return str(node.val)
return str(node.val) + '(' + dfs(node.left) + ')' + '(' + dfs(node.right) + ')'
return dfs(root)
解法三
def tree2str4(self, root):
path = ''
def preorder(node):
nonlocal path
if not node: return
path += str(node.val)
if not node.left and not node.right: return
path += '('
preorder(root.left)
path += ')'
if node.right:
path += '('
preorder(node.right)
path += ')'
preorder(root)
return path
解法四
如果只有左子树为空,则输出root()(right)
如果只有右子树为空,则输出root(left)
def tree2str5(self, root):
if not root: return ''
left_str = right_str = ''
if root.left or root.right:
left_str = '(' + self.tree2str(root.left) + ')'
# 只有右子树存在,才继续向下遍历;把右子树空时的'()'扔了
if root.right:
right_str = '(' + self.tree2str(root.right) + ')'
return str(root.val) + left_str + right_str
相关知识总结和思考
相关知识:
BFS:广度/宽度优先。其实就是从上到下,先把每一层遍历完之后再遍历一下一层。
可以使用Queue的数据结构。我们将root节点初始化进队列,通过消耗尾部,插入头部的方式来完成BFS。
二叉搜索树(BST)的特性:
- 若它的左子树不为空,则所有左子树上的值均小于其根节点的值
- 若它的右子树不为空,则所有右子树上的值均大于其根节点的值
- 它的左右子树也分别为二叉搜索树
递归与迭代的区别
递归:重复调用函数自身实现循环称为递归; 迭代:利用变量的原值推出新值称为迭代,或者说迭代是函数内某段代码实现循环;