树
树
树的概念
子树
当结点数>1时,其余结点分为互不相交的集合称为子树。
结点的度
该结点的子树数量。
树的度
树中各个结点度数的最大值。
叶子
所有度为0的结点,即子树为空的结点。
父结点
从根节点到该节点的路径上,离该结点最近的结点,称为该结点的父节点。
儿子结点
兄弟结点
同一个父结点的其他结点。
有序树
所有结点的儿子结点是有顺序的,否则为无序树。
森林
n个互不相交的树的集合。
结点的高度
结点到叶子结点的最长路径。比如上图树1根节点1的高度为3。
结点的深度
根结点到该结点的边个数。比如上图树1结点4的深度为1。
结点的层数
结点的深度加1。
树的高度
根结点的高度。
二叉树
一种特殊的树形结构,每个节点最多有两颗子树,且子树有左右之分。
二叉树特性
- 每个节点最多有两个子树,子树有左右之分;
- 在二叉树的第n层,最多有n^(n-1)个结点;
满二叉树
除叶子结点外,每个结点都有左右两个子结点。
完全二叉树
完全二叉树的最底层结点都连续靠左排列,除最底层的结点外,其他结点必须都有左右子结点。
完全二叉树可以看作是满二叉树的一个子集。
完全二叉树特性
对一颗有n个结点的完全二叉树按照从第一层到最后一层,并且每层都按从左到右的次序进行排序。那么:
- 任何一个结点编号为i的结点来说,它的左子结点为:2 * i;右子结点为:2 * 1+1;
- 任何一个结点编号为i的结点来说,它的父节点的编号为:i / 2;
为什么树3是完全二叉树,而树4却不是呢?请看二叉树的保存。
二叉树的实现
二叉树可以使用数组和链表来保存,先来看二叉树在数组中的保存方式。
根据完全二叉树的特性可知结点在数组中的存储方式如下图:
使用数组来保存二叉树,由于数组的内存地址是连续的,可以使用CPU预读机制,性能更高,而且也不需要额外开辟空间来维护指针。
但可以看出用数组存储非完全二叉树,会存在浪费空间的情况,这时可以使用队列来实现二叉树。
队列底层数据结构选择使用数组
所以完全二叉树可以用数组来实现,这也就是为什么还要分一个完全二叉树出来的原因。
数组实现
队列实现
二叉树的四种遍历方式
现在对下图中的树5进行遍历:
假如N代表根结点,L代表左子树,R代表右子树。
按照顺序遇到根节点就输出。
前序
NLR (根 -> 左 -> 右)
- 访问根结点;
- 前序遍历左子树;
- 前序遍历右子树;
结果:ABCDEFGHK
中序
LNR (左 -> 根 -> 右)
- 中序遍历左子树;
- 访问根结点;
- 中序遍历右子树;
结果:BDCAEHGKF
后序
LRN (左 -> 右 -> 根)
- 后序遍历左子树;
- 后序遍历右子树;
- 访问根结点;
结果:DCBHKGFEA
层次
Mysql
Java实现遍历方式
前中后序三种遍历算法的时间复杂度为:O(n)
public class BinaryTree<E> {
int resIndex = 0;
private static final int DEFAULT_CAPACITY = 50;
private Object[] res;
private int capacity = DEFAULT_CAPACITY;
public BinaryTree(int capacity) {
if(capacity <= 0){
this.capacity = DEFAULT_CAPACITY;
}else{
this.capacity = capacity;
}
this.res = new Object[this.capacity];
}
public BinaryTree() {
this.res = new Object[capacity];
}
public int getResIndex() {
return resIndex;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
public E[] getRes() {
E[] resData = (E[]) res;
resIndex = 0;
res = null;
res = new Object[this.capacity];
return resData;
}
/**
* 前序遍历:NLR 根 -> 左 -> 右
* @param rootTreeNode 根结点
*/
public void prev(TreeNode<E> rootTreeNode){
if(rootTreeNode == null){
return;
}
//左子树
TreeNode<E> leftTN = rootTreeNode.left;
//右子树
TreeNode<E> rightTN = rootTreeNode.right;
/**
* 顺序要按照NLR 根 -> 左 -> 右:
* 1.根:根结点先加进去
* 2.左:左子树不为空,就迭代
* 3.右:右子树不为空,就迭代
*/
//1.根:根结点先加进去
res[resIndex++] = rootTreeNode;
//2.左子树不为空,就迭代
if(leftTN != null){
prev(leftTN);
}
//3.右子树不为空,就迭代
if(rightTN != null){
prev(rightTN);
}
}
/**
* 中序遍历:LNR 左 -> 根 -> 右
* @param rootTreeNode 根结点
*/
public void mid(TreeNode<E> rootTreeNode){
if(rootTreeNode == null){
return;
}
//左子树
TreeNode<E> leftTN = rootTreeNode.left;
//右子树
TreeNode<E> rightTN = rootTreeNode.right;
/**
* 顺序要按照LNR 左 -> 根 -> 右:
* 1.左:左子树不为空,就迭代
* 2.根:根结点加进去
* 3.右:右子树不为空,就迭代
*/
//1.左子树不为空,就迭代
if(leftTN != null){
mid(leftTN);
}
//2.根:根结点加进去
res[resIndex++] = rootTreeNode;
//3.右子树不为空,就迭代
if(rightTN != null){
mid(rightTN);
}
}
/**
* 后序遍历:LRN 左 -> 右 -> 根
* @param rootTreeNode 根结点
*/
public void last(TreeNode<E> rootTreeNode){
if(rootTreeNode == null){
return;
}
//左子树
TreeNode<E> leftTN = rootTreeNode.left;
//右子树
TreeNode<E> rightTN = rootTreeNode.right;
/**
* 顺序要按照LNR 左 -> 右 -> 根:
* 1.左:左子树不为空,就迭代
* 2.右:右子树不为空,就迭代
* 3.根:根结点加进去
*/
//1.左子树不为空,就迭代
if(leftTN != null){
last(leftTN);
}
//2.右子树不为空,就迭代
if(rightTN != null){
last(rightTN);
}
//3.根:根结点加进去
res[resIndex++] = rootTreeNode;
}
static class TreeNode<E> {
E data;
TreeNode<E> left;
TreeNode<E> right;
public TreeNode(E data, TreeNode<E> left, TreeNode<E> right) {
this.data = data;
this.left = left;
this.right = right;
}
@Override
public String toString() {
return "TreeNode{" +
"data=" + data +"}";
}
}
public static void main(String[] args) {
//构建二叉树
TreeNode<String> D = new TreeNode<>("D", null, null);
TreeNode<String> C = new TreeNode<>("C", D, null);
TreeNode<String> B = new TreeNode<>("B", null, C);
TreeNode<String> H = new TreeNode<>("H", null, null);
TreeNode<String> K = new TreeNode<>("K", null, null);
TreeNode<String> G = new TreeNode<>("G", H, K);
TreeNode<String> F = new TreeNode<>("F", G, null);
TreeNode<String> E = new TreeNode<>("E", null, F);
TreeNode<String> A = new TreeNode<>("A", B, E);
BinaryTree<String> binaryTree = new BinaryTree<>(9);
binaryTree.prev(A);
System.out.println("前序遍历:" + Arrays.toString(binaryTree.getRes()));
binaryTree.mid(A);
System.out.println("中序遍历:" + Arrays.toString(binaryTree.getRes()));
binaryTree.last(A);
System.out.println("后序遍历:" + Arrays.toString(binaryTree.getRes()));
}
}