树论基础&二叉树
1 什么是树
概念
-
数据结构中的"树"(Tree)是一种非常重要的非线性数据结构,它模拟了具有树状结构特点的数据集合。在树形结构中,每个元素被称为一个节点(Node),节点之间通过特定的关系连接,这些关系通常被描述为"父子"关系。
-
树是由n(n≥0)个有限节点组成一个具有层次关系的集合。它有一个特殊的节点,称为根节点(Root Node),其余节点分成m(m≥0)个互不相交的有限集T1, T2, ..., Tm,其中每一个子集Ti(1≤i≤m)又是一棵符合本定义的树,称为根的子树(Subtree)。
特性
- 层次性:树中的节点按照层次组织,根节点位于最顶层,叶子节点位于最底层。
- 无环性:在树中,除了根节点外,每个节点有且仅有一个父节点;没有节点是其自身的祖先或后代。
- 子树不相交性:任意两个子树的节点之间没有交集。
2 重要术语
- 节点 (Node)
- 树的每个元素都是一个节点。
- 节点可能包含一个值或一组值,以及指向其子节点的链接。
- 根节点 (Root Node)
- 树中唯一没有父节点的节点。
- 它通常是树的入口点。
- 子节点 (Child Node)
- 一个节点可以有零个或多个子节点。
- 子节点通过边(Edge)与父节点相连。
- 父节点 (Parent Node)
- 子节点的上一级节点。
- 除了根节点外,每个节点都有一个父节点。
- 兄弟节点 (Sibling Nodes)
- 具有相同父节点的节点。
- 叶子节点 (Leaf Node)
- 没有子节点的节点。
- 它们位于树的最低层。
- 度 (Degree)
- 一个节点的子节点数。
- 叶子节点的度为0。
- 示例:在二叉树中,每个节点最多有两个子节点(一个左子节点和一个右子节点),因此二叉树中节点的度最大为2。但在一般的树(也称为N叉树)中,节点的度可以大于2,具体取决于树的定义和上下文。
- 树的度 (Degree of a Tree)
- 树中所有节点的度的最大值。
- 层次 (Level)
- 从根节点开始,根节点为第1层,它的子节点为第2层,依此类推。
- 深度 (Depth)或高度 (Height)
- 树中节点的最大层次数。
- 根节点的深度为0,其直接子节点的深度为1,依此类推。
- 路径 (Path)
- 从一个节点到另一个节点所经过的节点序列。
- 路径长度是路径上的节点数减一。
- 森林 (Forest)
- 一个或多个互不相交的树的集合。
- 结点的高度:结点到叶子结点的最长路径
- 结点的深度:根结点到该结点的边个数
- 结点的层数:结点的深度加1
- 树的高度:根结点的高度
3 不同的树
- 二叉树 :每个节点最多有两个子节点的树。
- 每个节点最多有两个子节点,通常称为左子节点和右子节点。
- 子树之间不能相交。
- 除了根节点外,每个节点有且仅有一个父节点。
- 一颗N个结点的树有N-1条边。
- 一个节点的子树的个数称为节点的度,普通二叉树中节点的度最大为2。
- 二叉搜索树(BST):二叉树的一种特殊形式,其中对于树中的每个节点,其左子树上所有节点的值都小于它的值,而右子树上所有节点的值都大于它的值。
- 平衡二叉树:一种特殊的二叉搜索树,其中任意节点的两个子树的高度差最多为1。常见的平衡二叉树有AVL树和红黑树。
- 完全二叉树 :除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干节点。
- 编号性质:对于深度为h的完全二叉树,它的节点从1到2^h-1依次编号。其中,对于任意节点i,如果它的编号为i,则它的左子节点编号为2i,右子节点编号为2i+1。
- 父子关系:对于任意节点i,如果它的编号为i,则它的父节点编号为i/2(向下取整)。
- 高度:完全二叉树的高度为log(n+1)(以2为底),其中n为节点数。
- 存储方式:完全二叉树可以使用数组来存储,从而实现快速的访问和修改。
- 满二叉树 :除叶子结点外,每个结点都有左右两个子结点。
- 每一层的节点数都达到了最大值。
- 节点要么是叶子节点(没有子节点),要么是度为2的节点(有两个子节点)。
- N叉树:每个节点可以有N个子节点的树,其中N是一个大于1的整数。
- B树和B+树:用于数据库和文件系统的平衡多路搜索树,它们允许每个节点有多个子节点和关键字,用于高效地插入、删除和搜索数据。
- 堆(Heap):一种特殊的树形数据结构,通常用于实现优先队列。堆是一种完全二叉树,它满足堆的性质:父节点的值总是大于或等于(大顶堆)或小于或等于(小顶堆)其子节点的值。
4 二叉树的遍历
4.1 前序遍历
前序遍历顺序:根节点、左节点、右节点
JAVA
/**
* 前序遍历
* @param node
*/
public void preOrder(MyBinaryTreeNode<T> node){
print(node);
if (node.left != null){
preOrder(node.left);
}
if (node.right != null){
preOrder(node.right);
}
}
4.2 中序遍历
中序遍历顺序:左节点、跟节点、右节点
JAVA
/**
* 中序遍历
*/
public void middleOrder(MyBinaryTreeNode<T> node){
if (node == null){
return;
}
if (node.left != null){
middleOrder(node.left);
}
print(node);
if (node.right != null){
middleOrder(node.right);
}
}
4.3 后续遍历
后续遍历顺序:左节点、右节点、根节点
JAVA
/**
* 后续遍历
*/
public void postOrder(MyBinaryTreeNode<T> node){
if (node == null){
return;
}
if (node.left != null){
postOrder(node.left);
}
if (node.right != null){
postOrder(node.right);
}
print(node);
}
4.4 层次遍历
层次遍历:也叫横向遍历
java
/**
* 层级遍历
*/
public void transverseSort(MyBinaryTreeNode<T> node){
if (node == null){
return;
}
print(node);
ArrayQueue<MyBinaryTreeNode<T>> queue = new ArrayQueue<>(10);
queue.add(node);
while (!queue.isEmpty()){
MyBinaryTreeNode<T> remove = queue.remove(0);
if (remove.left != null){
print(remove.left);
queue.add(remove.left);
}
if (remove.right != null){
print(remove.right);
queue.add(remove.right);
}
}
}
4.5 完整代码
JAVA
package cn.zxc.demo.leetcode_demo.advanced_data_structure;
import com.sun.jmx.remote.internal.ArrayQueue;
/**
* 二叉树
* A
* / \
* B E
* \ \
* C F
* / /
* D G
* / \
* H K
* 前序遍历:
* A B C D E F G H K
* 中序遍历:
* B D C A E H G K F
* 后序遍历:
* D C B H K G F E A
* 层级遍历:
* A B E C F D G H K
*/
public class MyBinaryTreeDemo<T> {
/**
* 前序遍历
* @param node
*/
public void preOrder(MyBinaryTreeNode<T> node){
print(node);
if (node.left != null){
preOrder(node.left);
}
if (node.right != null){
preOrder(node.right);
}
}
/**
* 中序遍历
*/
public void middleOrder(MyBinaryTreeNode<T> node){
if (node == null){
return;
}
if (node.left != null){
middleOrder(node.left);
}
print(node);
if (node.right != null){
middleOrder(node.right);
}
}
/**
* 后续遍历
*/
public void postOrder(MyBinaryTreeNode<T> node){
if (node == null){
return;
}
if (node.left != null){
postOrder(node.left);
}
if (node.right != null){
postOrder(node.right);
}
print(node);
}
/**
* 层级遍历
*/
public void transverseSort(MyBinaryTreeNode<T> node){
if (node == null){
return;
}
print(node);
ArrayQueue<MyBinaryTreeNode<T>> queue = new ArrayQueue<>(10);
queue.add(node);
while (!queue.isEmpty()){
MyBinaryTreeNode<T> remove = queue.remove(0);
if (remove.left != null){
print(remove.left);
queue.add(remove.left);
}
if (remove.right != null){
print(remove.right);
queue.add(remove.right);
}
}
}
/**
* 打印
* @param node
*/
public void print(MyBinaryTreeNode<T> node){
if(node == null){
return;
}
System.out.print(node.data + " ");
}
public static void main(String[] args) {
MyBinaryTreeNode<String> D = new MyBinaryTreeNode<String>("D", null, null);
MyBinaryTreeNode<String> H = new MyBinaryTreeNode<String>("H", null, null);
MyBinaryTreeNode<String> K = new MyBinaryTreeNode<String>("K", null, null);
MyBinaryTreeNode<String> C = new MyBinaryTreeNode<String>("C", D, null);
MyBinaryTreeNode<String> G = new MyBinaryTreeNode<String>("G", H, K);
MyBinaryTreeNode<String> B = new MyBinaryTreeNode<String>("B", null, C);
MyBinaryTreeNode<String> F = new MyBinaryTreeNode<String>("F", G, null);
MyBinaryTreeNode<String> E = new MyBinaryTreeNode<String>("E", null, F);
MyBinaryTreeNode<String> A = new MyBinaryTreeNode<String>("A", B, E);
MyBinaryTreeDemo<String> myBinaryTreeDemo = new MyBinaryTreeDemo<>();
System.out.println("前序遍历:");
myBinaryTreeDemo.preOrder(A); // A B C D E F G H K
System.out.println();
System.out.println("中序遍历:"); // B D C A E H G K F
myBinaryTreeDemo.middleOrder(A);
System.out.println();
System.out.println("后序遍历:"); // D C B H K G F E A
myBinaryTreeDemo.postOrder(A);
System.out.println();
System.out.println("层级遍历:");
myBinaryTreeDemo.transverseSort(A);
System.out.println();
}
}
class MyBinaryTreeNode<T> {
T data;
MyBinaryTreeNode<T> left;
MyBinaryTreeNode<T> right;
public MyBinaryTreeNode(T data) {
this.data = data;
}
public MyBinaryTreeNode(T data, MyBinaryTreeNode<T> left, MyBinaryTreeNode<T> right) {
this.data = data;
this.left = left;
this.right = right;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public MyBinaryTreeNode<T> getLeft() {
return left;
}
public void setLeft(MyBinaryTreeNode<T> left) {
this.left = left;
}
public MyBinaryTreeNode<T> getRight() {
return right;
}
public void setRight(MyBinaryTreeNode<T> right) {
this.right = right;
}
}