构造二叉树
树结构是一类重要的非线性数据结构。直观来看,树是以分支关系定义的层次结构。树结构在客观世界广泛存在,如人类社会的族谱和各种社会组织机构都可用树来形象表示。
树在计算机领域中也得到广泛应用,尤以二叉树最为常用。如在操作系统中,用树来表示文件目录的组织结构。在编译系统中,用树来表示源程序的语法结构。在数据库系统中,树结构也是信息的重要组织形式之一
对于二叉树的定义以及数据结构,我们就不再赘述,今天主要讲一下如何构造一颗二叉树
二叉树的遍历
查看leetcode
关于构造二叉树的题目,发现他们都是基于二叉树的前、中、后序遍历去构造,那么在我们先了解一下二叉树的遍历
首先我们定义一个二叉树的数据结构,这里以Go为例:
1 | type TreeNode struct { |
这里我们引入一个框架,通过框架一次性解决二叉树遍历问题:
1 | func traverse(root *TreeNode) { |
通过以上框架,二叉树的前、中、后序遍历,在对应位置添加对应代码即可,以leetcode
114题「二叉树的前序遍历」为例:
1 | func preorderTraversal(root *TreeNode) []int { |
二叉树的构造
通过前序和中序遍历结果构造二叉树
leetcode
第105题「从前序与中序遍历序列构造二叉树」
要构造二叉树,首先第一步就是要找出二叉树的根节点,然后递归构造左右子树即可,根据上文所述的二叉树遍历框架,我们可以得到preorder
和inorder
数组中的元素分布有如下特点:
由上图可知,根节点就是preorder[0]
,因为二叉树特性,无论怎么遍历,左右子树个数始终相同,使用跟节点去inoder
数组中获取左右子树的个数,将preorder
和inoder
数组划分为两部分,递归构造左右子树即可
示例代码:
1 | func buildTree(preorder []int, inorder []int) *TreeNode { |
通过中序与后序遍历序列构造二叉树
leetcode
第106题「从中序与后序遍历序列构造二叉树」
类似前序相关思路,先查看他们的特点:
与前序遍历不同的是,原本根节点从preorder[0]
变成了postorder
最后一个元素,其余部分基本一样没什么变化
示例代码:
1 | func buildTree106(inorder []int, postorder []int) *TreeNode { |
通过后序和前序遍历结果构造二叉树
leetcode
第889题「从中序与后序遍历序列构造二叉树」
这道题与前面两道题有一个本质上的区别:通过前序+中序或者后序+中序都可以确定一个唯一二叉树,但是前序+后序无法确定一颗唯一的二叉树,为什么呢?
前两道题,可以通过前序或者后序遍历结构找到根节点,然后根据中序遍历确定左右子树。这道题你可以确定根节点,但还是无法确切的知道左右子树有那些节点
举个例子,题目输入:
1 | preorder = [1,2,3], postorder = [3,2,1] |
下面这两棵树都符合条件的,但是他们的结果完全不同:
虽然无法确定唯一的二叉树,但还是可以还原二叉树,整体逻辑与前两图差别不大:
- 首先把前序遍历结果的第一个元素
- 然后把前序遍历结果的第二个元素作为左子树的根节点的值
- 在后序遍历结果中寻找左子树根节点的值,从而确定了左子树的索引边界,进而确定右子树的索引边界,递归构造左右子树即可
- 后序遍历思路以此类推即可
示例代码:
1 | func constructFromPrePost(preorder []int, postorder []int) *TreeNode { |