二叉树的种类
在我们解题过程中二叉树有两种主要的形式:满二叉树和完全二叉树。
满二叉树
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
如图所示:
这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树。
完全二叉树
什么是完全二叉树?
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。
二叉搜索树
前面介绍的树,都没有数值的,而二叉搜索树是有数值的了,二叉搜索树是一个有序树。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
下面这两棵树都是搜索树
平衡二叉搜索树
平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
最后一棵 不是平衡二叉树,因为它的左右两个子树的高度差的绝对值超过了1。
二叉树的存储方式
二叉树可以链式存储,也可以顺序存储。
那么链式存储方式就用指针, 顺序存储的方式就是用数组。
顾名思义就是顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在散落在各个地址的节点串联一起。
链式存储如图:
链式存储是大家很熟悉的一种方式,那么我们来看看如何顺序存储呢?
其实就是用数组来存储二叉树,顺序存储的方式如图:
用数组来存储二叉树如何遍历的呢?
如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
二叉树的遍历方式
二叉树主要有两种遍历方式:
- 深度优先遍历:先往深走,遇到叶子节点再往回走。
- 广度优先遍历:一层一层的去遍历。
这两种遍历是图论中最基本的两种遍历方式。
那么从深度优先遍历和广度优先遍历进一步拓展,才有如下遍历方式:
- 深度优先遍历
- 前序遍历(递归法,迭代法)
- 中序遍历(递归法,迭代法)
- 后序遍历(递归法,迭代法)
- 广度优先遍历
- 层次遍历(迭代法)
在深度优先遍历中:有三个顺序,前中后序遍历, 有同学总分不清这三个顺序,经常搞混,我这里教大家一个技巧。
这里前中后,其实指的就是中间节点的遍历顺序,只要大家记住 前中后序指的就是中间节点的位置就可以了。
看如下中间节点的顺序,就可以发现,中间节点的顺序就是所谓的遍历方式
- 前序遍历:中左右
- 中序遍历:左中右
- 后序遍历:左右中
如下图:
二叉树的定义
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
一、LeetCode144. 二叉树的前序遍历
1:题目描述(144. 二叉树的前序遍历)
给你二叉树的根节点 root
,返回它节点值的 前序 遍历。
2:解题思路
解法一:递归法
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def preorderTraversal(self, root):
# # 前序遍历:中左右
# 使用递归遍历,需要确定的三个条件
# 1:确定递归函数的参数和返回值
# 2:确定终止条件
# 3:确定单层递归的逻辑
def traversal(cur, res):
if cur == None: # 当节点为空,返回到上个节点
return
res.append(cur.val) # 将父节点添加到res
traversal(cur.left, res) # 遍历左节点
traversal(cur.right, res) # 遍历右节点
res = []
cur = root
traversal(cur, res)
return res
解法二:迭代法
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def preorderTraversal(self, root):
# # 前序遍历:中左右
# 迭代法
# 使用栈来实现
stack = [] # 定义一个栈
res = [] # 存遍历节点的数组
if not root:
return res
stack.append(root) # 将根节点添加到栈中
while stack:
node = stack.pop() # 将栈中最后一个节点弹出
res.append(node.val) # 并把节点的值加入到res中
# 然后将节点的左右子节点添加到栈中
# 因为前序遍历的顺序是:中左右,栈是先进后出,所以需要先将右节点先入栈,在将左节点入栈
if node.right:
stack.append(node.right) # 当右节点不为空时,先将右节点入栈
if node.left:
stack.append(node.left) # 当左节点不为空时,再将左节点入栈
return res
二、LeetCode145. 二叉树的后序遍历
1:题目描述(145. 二叉树的后序遍历)
给你一棵二叉树的根节点 root
,返回其节点值的 后序遍历 。
2:解题思路
解法一:递归法
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def postorderTraversal(self, root):
# 后序遍历:左右中
# 使用递归遍历,需要确认三个条件
# 1:确定递归函数的参数和返回值
# 2:确定终止条件
# 3:确定单层递归的逻辑
def traversal(cur, res):
if cur == None:
return
traversal(cur.left, res)
traversal(cur.right, res)
res.append(cur.val)
res = []
traversal(root, res)
return res
解法二:迭代法
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def postorderTraversal(self, root):
# 后序遍历:左右中
# 迭代法
# 使用栈来实现
stack = []
res = []
if not root:
return res
# 因为后序遍历的顺序是:左右中,可以将顺序改为中右左,最后再将得到的res进行一个翻转即可
stack.append(root) # 将根节点root加入栈stack中
while stack:
node = stack.pop() # 将根节点从栈中弹出
res.append(node.val) # 将根节点的值,添加到数组res中
# 我们添加到res的顺序是中右左,栈是先进后出,所以先将左节点入栈,再将右节点入栈
if node.left:
stack.append(node.left) # 左节点不为空,则入栈
if node.right:
stack.append(node.right) # 右节点不为空,则入栈
# 此时res中的结果是按照中右左的顺序进行遍历得到的结果,需要将res中的元素进行翻转
res.reverse() # 翻转数组
return res
三、LeetCode94. 二叉树的中序遍历
1:题目描述(94. 二叉树的中序遍历)
给定一个二叉树的根节点 root
,返回 它的 中序 遍历 。
2:解题思路
解法一:递归法
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def inorderTraversal(self, root):
# 中序遍历:左中右
def traversal(cur, res):
if cur == None:
return
traversal(cur.left, res) # 遍历左节点
res.append(cur.val) # 左节点遍历完成后,将根节点加入res中
traversal(cur.right, res) # 遍历右节点
res = []
traversal(root, res)
return res
解法二:迭代法
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def inorderTraversal(self, root):
# 中序遍历:左中右
# 迭代法
# 使用栈和指针来实现,栈用来存放遍历过的元素,指针进行遍历二叉树
stack = []
res = []
cur = root
if not root: # 当根节点为空,直接返回[]
return res
while cur or stack:
# 先迭代访问最底层的左子树节点
if cur != None: # 当前节点不为空
stack.append(cur) # 将当前节点入栈
cur = cur.left # 再指向当前节点的左节点
# 到达最底层左子节点后,处理栈顶元素
else: # 当前节点为空了
cur = stack.pop() # 从栈中弹出节点
res.append(cur.val) # 将节点的值加入到res中
cur = cur.right # 在将cur指向当前节点的右节点
return res