树
树是由n(n≥0)个有限节点组成一个有层次关系的集合。把它叫作树是因为它看起来像一个倒挂的树。它具有以下特点:
- 每个节点有零个或多个子节点;
- 没有父节点的节点称为根节点;
- 每一个非根节点有且只有一个父节点;
- 除了根节点外,每个子节点可以分为多个不相交的树;
树的高度、深度、层
- 节点的高度:节点到叶子节点的最长路径(边数)
- 节点的深度:根节点到这个节点所经历的边的个数
- 节点的层数:节点的深度+1
- 树的高度:根节点的高度 高度从下往上度量(计数起点是0);深度从上往下度量;层数同深度极端不过计数起点是1。
二叉树
二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个节点,分别是左子节点和右子节点。不过,二叉树并不要求每个节点都有两个节点。
- 满二叉树:叶子节点都在最底层,除了叶子节点之外,每个节点都有左右两个子节点。
- 完全二叉树:叶子节点都在最底两层,最后一层的节点从左到右是连续的,没有断开。
存储二叉树
存储二叉树,有两种方法:一种是基于指针或者引用的二叉链式存储法;一种是基于数组的顺序存储法。
首先看比较简单直观的链式存储法。每个节点有三个字段,其中一个存储数据,另外两个指向左右子节点的指针。只要拎出来根节点,就能通过左右子节点的指针把整棵树串起来。
然后是基于数组的顺序存储法。根节点存储在下标i=1的位置,左子节点存储在下标2*i=2的位置,右子节点存储在2*i+1=3的位置,以此类推。如果当前节点为空,则对应下标置空。所以,如果某棵二叉树是一棵完全二叉树,那用数组存储无疑是最节省内存的一种方式。
二叉树的遍历
如何将所有节点都遍历打印出来呢?经典的方法有三种:前序遍历、中序遍历和后序遍历。
- 前序遍历是指,对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印它的右子树。
# 递归
def preorderTraversal(root):
def preorder(root):
if root == None: return
res.append(root.val)
preorder(root.left)
preorder(root.right)
res = []
preorder(root)
return res
# 迭代遍历
def preorderTraversal(root):
res = []
stack = []
while root or stack:
while root:
res.append(root.val)
stack.append(root)
root = root.left
root = stack.pop()
root = root.right
return res
- 中序遍历是指,对于树中的任意节点来说,先打印这个节点的左子树,再打印它本身,最后打印它的右子树。
# 递归
def inorderTraversal(root):
def inorder(root):
if root == None: return
inorder(root.left)
res.append(root.val)
inorder(root.right)
return res
res = []
inorder(root)
return res
# 迭代遍历
def inorderTraversal(root):
res = []
stack = []
while root or stack:
while root:
stack.append(root)
root = root.left
root = stack.pop()
res.append(root.val)
root = root.right
return res
- 后序遍历是指,对于树中的任意节点来说,先打印这个节点它的左子树,然后打印它的右子树,最后打印这个节点本身。
# 递归
def postorderTraversal(root):
def postorder(root):
if root == None: return
postorder(root.left)
postorder(root.right)
res.append(root.val)
return res
res = []
postorder(root)
return res
# 迭代遍历,后序遍历需要主要把根节点放到最后,所以需要一个prev节点记录已遍历过的节点。且如果右子树未遍历,根节点需要二次入栈。
def postorderTraversal(root):
res = []
stack = []
# 记录最新已遍历存入res中的节点
prev = None
while root or stack:
while root:
stack.append(root)
root = root.left
root = stack.pop()
# 如果不存在右子树或者右子树已经遍历过,则将val存入res中,将prev置为当前节点,且将root置为None。
if not root.right or prev == root.right:
res.append(root.val)
prev = root
root = None
# 如果存在右子树,则将当前节点再次压入stack中。(因为根节点要放到最后)
else:
stack.append(root)
root = root.right
return res
- 按层遍历:即逐层地,从左到右访问所有节点
# 使用双向队列,遍历过的出列,下一层入列
def levelOrder(root):
if not root: return []
res = []
def level(root):
queue = collections.deque()
queue.append(root)
while queue:
# 计算当前层级的节点数
l = len(queue)
tmp = []
for i in range(l):
node = queue.popleft()
tmp.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
res.append(tmp)
level(root)
return res