1.树的基本概念
树状图是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。它看起来像一颗倒挂的树,说明它的特点:根朝上,叶朝下
- 子节点和父节点(相对概念)
- 边:从父节点到子节点的连线
- 兄弟节点:父节点相同的节点互为兄弟节点
- 树叶、分支节点:没有子节点的节点称为树叶,树中的其余节点称为分支节点(分支节点可只有一个分支)
- 祖先和子孙:基于父节点/子节点关系和传递性,可以确定相应的传递关系,称为祖先关系或子孙关系
- 度数:一个节点的子节点个数称为该节点的度数
- 路径、路径长度:
从一个祖先节点到其子孙节点的一系列边称为树中一条路径(从一棵树的根到树中任一个节点都有唯一路径)
路径中边的条数称为路径的长度,认为每个节点到自身有长0的路径 - 节点的层数:
树根到节点的路径长度是该节点的层数
节点都有层数,根所在的层为0- 高度(或深度)
树的高度或深度是树中节点的最大层数(最长路径的长度)加1
空树高度为0,只有根节点的树高度为1
- 高度(或深度)
2.二叉树的定义
- 二叉树是一种树形结构:(特点)
与每个节点关联的子节点至多有两个(0,1,2)
每个节点的子节点有关联位置关系 - 定义:
二叉树是节点的有限集合,该集合或为空集,或由一个根元素和两颗不相交的二叉树组成(递归定义)
二叉树的两颗字数分别称为它的左子树和右子树 - 二叉树的5种基本形状:
2.1二叉树分类
1.满二叉树
2.完全二叉树
3.扩充二叉树
满 | 完全 | 完满 |
---|---|---|
所有节点有两个子节点 | 除最后一层全满 | 每个节点两个子节点 |
Height=Level | 最后一层左往右排 |
- 满和完全的二叉树
- 满二叉树:树中每个分支节点(非叶节点)都有两颗非空子树
- 完全二叉树:除最下两层外,其余节点度数都是2,如果最下面的节点不满,则所有空位都在右边,左边没有空位,如下图:
- 扩充二叉树(由已有非空二叉树生成的一种二叉树):
是原二叉树的最小节点扩充,使原树中所有节点的度数都变成2
- 二叉树的性质:
- 1.非空二叉树第i层上之多有2i个节点(i ≥ 0)
- 2.高度为k的二叉树至多有2k-1个节点(k ≥ 0)
- 3.对任何非空二叉树T,若其叶节点个数为n0,度数为2的节点
个数为n2,则n0 = n2 + 1 - 4.n个节点的完全二叉树的高度k = ⎡log2(n+1)⎤
- 5.满二叉树里的叶节点比分支节点多一个
- 二叉树的性质:
- 二叉树的数据结构
- 基本操作
- 创建二叉树
- 一颗二叉树或为空(用None表示),或是两颗已有二叉树和要存在树根节点的一项数据,构造起的根节点代表构造出的二叉树:BiTree(dat,left,right)
- 判断树空:is_empty(titree)
- 访问操作,访问二叉树的组成部分
- 访问二叉树的根节点数据元素:data()
- 取得一颗二叉树的左右子树:right(),left()
3.二叉树的顺序存储
- 基本操作
public class ArrayBinaryTree<T> {
private static final int DEFAULT_DEEP = 5;
private int deep;
private int capacity;
private Object[] nodes;
public ArrayBinaryTree() {
this(DEFAULT_DEEP);
}
public ArrayBinaryTree(int deep) {
this.deep = deep;
nodes = new Object[capacity = (int) Math.pow(2, deep) - 1];
}
public ArrayBinaryTree(int deep, T rootData) {
this(deep);
nodes[0] = rootData;
}
public void add(int parentIndex, T data, boolean left) {
if (data == null) {
throw new NullPointerException();
}
if (nodes[parentIndex] == null) {
throw new NoSuchElementException();
}
if (left) {
nodes[parentIndex * 2 + 1] = data;
} else {
nodes[parentIndex * 2 + 2] = data;
}
}
public boolean isEmpty() {
return nodes[0] == null;
}
/**
* 获取索引为index节点的父节点
*
* @param index
* @return
*/
public T getParent(int index) {
if (index == 0) {
return null;
}
return (T) nodes[(index - 1) / 2];
}
/**
* 获取索引为index的右节点
*
* @param index
* @return
*/
public T getRight(int index) {
if (2 * index + 1 >= capacity) {
return null;
}
return (T) nodes[index * 2 + 2];
}
/**
* 获取索引为index的左节点
*
* @param index
* @return
*/
public T getLeft(int index) {
if (2 * index + 1 >= capacity) {
return null;
}
return (T) nodes[2 * index + 1];
}
public T getRoot() {
return (T) nodes[0];
}
public int indexOf(T data) {
for (int i = 0; i < capacity; i++) {
if (nodes[i].equals(data)) {
return i;
}
}
return -1;
}
}
4.二叉树的链式存储
public class TwoLinkedBinaryTree<T> {
class Node {
Object data;
Node left;
Node right;
public Node() {
}
public Node(Object data) {
this.data = data;
}
public Node(Object data, Node left, Node right) {
this.data = data;
this.left = left;
this.right = right;
}
@Override
public String toString() {
return data + "";
}
}
private Node root;
public TwoLinkedBinaryTree() {
this.root = new Node();
}
public TwoLinkedBinaryTree(T data) {
this.root = new Node(data);
}
public Node addNode(Node parent, T data, boolean left) {
if (data == null) {
throw new NullPointerException();
}
if (parent == null) {
throw new IllegalStateException("节点为null,不能添加子节点");
}
if (left && parent.left != null) {
throw new IllegalStateException(parent + "节点已经存在左节点");
}
if (!left && parent.right != null) {
throw new IllegalStateException(parent + "节点已经存在右节点");
}
Node node = new Node(data);
if (left) {
parent.left = node;
} else {
parent.right = node;
}
return node;
}
public boolean isEmpty() {
return root.data == null;
}
public Node getRoot() {
return root;
}
public T getLeft(Node parent) {
return parent == null ? null : (T) parent.left.data;
}
public T getRight(Node parent) {
return parent == null ? null : (T) parent.right.data;
}
}
5.二叉树的遍历
树的遍历主要有两种,一个是深度优先遍历,一个是广度优先遍历。
深度优先遍历又有三种:前序、中序、后序遍历。
前序、中序、后序遍历区别在于访问节点的是前面访问还是中间访问或是后面访问,具体代码如下:
5.1前序遍历
private void preorder(Node<T> node) {
if (node == null) {
return;
}
System.out.println(node.value);//前面访问节点
preorder(node.left);
preorder(node.right);
}
5.2中序遍历
private void inorder(Node<T> node) {
if (node == null) {
return;
}
inorder(node.left);
System.out.println(node.value);//中间访问节点
inorder(node.right);
}
5.3后序遍历
private void postorder(Node<T> node) {
if (node == null) {
return;
}
postorder(node.left);
postorder(node.right);
System.out.println(node.value);//最后访问节点
}
5.4广度优先遍历
广度优先遍历也叫做按层遍历(一层一层的遍历节点):
public void levelorder() {
if (root == null)
return;
Deque<Node<T>> queue = new ArrayDeque<>();
queue.addLast(root);
while (!queue.isEmpty()) {
Node<T> node = queue.removeFirst();
System.out.println(node.value);
if (node.left != null) {
queue.addLast(node.left);
}
if (node.right != null) {
queue.addLast(node.right);
}
}
}