vlambda博客
学习文章列表

二叉树基础(上):如何遍历二叉树?

二叉树

二叉树,顾名思义,每个结点最多有两个“叉”,也就是两个子节点,分别是左子节点和右子节点。不过,二叉树并不要求每个结点都必须有两个子节点,可以只有左子节点或者只有右子节点。

最坏情况下,二叉树退化成链表,也就是每个节点只有左子节点或右子节点,这种情况下,二叉树就跟链表没有什么区别了。

满二叉树,除叶子节点外,每一层每一个节点都两个子节点的二叉树。满二叉树很好理解,只要看这颗二叉树的每一个节点有没有缺失,除了叶子节点外,其他的节点的有没有两个子节点就行了。

完全二叉树,最后一层叶子节点都靠左排列,并且除了最后一层,其他层的节点个数达到最大。

如何存储二叉树?

有两种存储方式,一种是基于链表的链式存储法,一种是基于数组顺序存储法。

链式存储

我们先来看链式存储法,每个节点有三个字段,其中一个存储数据,另外两个存储指向左右子节点的指针。链式存储法看起来很直观,大部分的代码都是通过链表这种结构实现的。

顺序存储

我们在做 leetcode 二叉树部分的代码时,能发现每次题目给的输入出都是数组这种形式,这其实就是二叉树的顺序存储方式。 

  
    
    
  
普通二叉树 3 / \ 9 20 / \     15 6[3, 9, 20, null, null, 15, 6]完全二叉树 1 / \ 2 3 / \ /4 5 6[1, 2, 3, 4, 5, 6]

我们不难看出,二叉树的顺序存储是按照从上往下,从左往右依次访问二叉树节点的,空节点的地方在数组中存储 null

我们发现,二叉树的顺序存储,在数组中出现元素为空的情况,这样就浪费了大量的存储空间。

所以,如果一棵完全二叉树,用数组存储无疑是最节省内存的一种方式。因为数组的存储方式不需要像链表那样,要存储额外的左右子节点的指针。这也是为什么完全二叉树要求最后一层的子节点都靠左的原因。

二叉树的遍历

前中序三种遍历方式的区别就是访问根节点的顺序

  • 前序遍历, 根-左-右
  • 中序遍历, 左-根-右
  • 后序遍历, 左-右-根
  • 层序遍历,一层一层地遍历

实际上,二叉树的前、中、后序遍历就是一个递归的过程。比如,前序遍历,其实就是先打印根节点,然后递归地打印左子树,最后递归地打印右子树

二叉树遍历的时间复杂度

二叉树每个节点最多会被访问两次,所以遍历操作的时间复杂度,跟节点个数 n 成正比,也就是说二叉树遍历的时间复杂度是 O(n)。