数据结构 树(Tree)
1. 树的简介
树(Tree)是一种重要的非线性数据结构,它以分层方式组织数据。树由一组以边连接的节点组成,其中一个节点被称为根节点,其他节点分层次地连接到根节点,形成了层级结构。
2. 树的基本概念
以下概念均以二叉树为主要对象进行解释、介绍和绘图。
2.1 根节点
树的顶层节点,没有父节点,根节点即为(Root)。
2.2 父节点
一个节点连接到它的直接上层节点,称为它的父节点(Parent)。
此时父节点是相对而谈的,比如图中parent节点是root的children节点,却是children的parent节点。
2.3 子节点
一个节点连接到它的直接下层节点,称为它的子节点(Children)。一个节点可以有零个或多个子节点。
此时父子节点是相对而谈的,比如图中parent节点是root的children节点,却是children节点的parent节点。
2.4 叶子节点
没有子节点的节点称为叶子节点或叶节点(Leaf)。
2.5 兄弟节点
具有相同父节点的节点互称为兄弟节点(Siblings)。
2.6 深度
从根节点到某个节点的唯一路径上的边数(Depth)。
此时根节点的深度为1,每向下一层,深度随即+1。
2.7 高度
树中任意节点的最大深度(Height)。
叶子节点的深度为0,从下往上逐层递增,以此类推。
2.9 子树
树中的任意一个节点和它的后代节点,以及与这些节点之间的连接,组成的树称为原树的子树(Subtree)。
3. 树的常见类型
3.1 二叉树
每个节点最多有两个子节点的树(Binary Tree)。
3.1.1 节点数量与边数量关系
对于一棵有 n 个节点的二叉树。
最多有 n-1 条边(有且只有一边子树)。最少有 ⌈n/2⌉ 条边(每层的节点都占满),其中 ⌈x⌉ 表示对 x 向上取整。
3.1.2. 节点数量与树的高度关系
对于一棵有 n 个节点的二叉树。
最小高度为 ⌈log₂(n+1)⌉ - 1(每层的节点都占满)。最大高度为 n-1(有且只有一边子树)。
3.2 二叉搜索树
一种特殊的二叉树 (Binary Search Tree),左子树的节点都小于根节点,右子树的节点都大于根节点。
二叉搜索树不一定是平衡二叉树,但是平衡二叉树一定是二叉搜索树。
3.3 平衡二叉树
一种高度平衡的二叉树(AVL),确保在插入或删除操作后,树的高度不会过高 (Balanced Binary Tree)。
二叉搜索树不一定是平衡二叉树,但是平衡二叉树一定是二叉搜索树。
3.4 完全二叉树
除了最后一层,每一层都是满的,并且最后一层的节点都尽量靠左排列 (Complete Binary Tree)。
有 ⌈n/2⌉ 条边,且有 ⌈log₂(n+1)⌉ - 1高。
完全二叉树不一定是满二叉树,但是满二叉树一定是完全二叉树。
3.4.1 完全二叉树的性质
对于一棵有 n 个节点的完全二叉树:
若从上至下、从左至右编号节点,则节点 i 的左子节点为 2i,右子节点为 2i + 1。
若从上至下、从左至右编号节点,则节点 i 的父节点为 i/2(向下取整)。
3.5 满二叉树
每个节点要么是叶子节点,要么有两个子节点的二叉树 (Full Binary Tree)。
完全二叉树不一定是满二叉树,但是满二叉树一定是完全二叉树。
3.6 N叉树
每个节点最多有N个子节点的树 (N-ary Tree)。
如下图所示,是一个三叉树,即有N=3。
3.7 哈夫曼树
是一种用于数据压缩的树形结构(Huffman Tree),它通过根据数据出现的频率构建一棵具有最小编码长度WPL的二叉树,从而实现对数据的高效压缩和解压缩。
越频繁的数据(权重越高)越靠近霍夫曼树的上层(更短的编码),越不频繁的数据(权重越低)更靠近霍夫曼树的底层(更长的编码)。
4. 树的存储
4.1 链式存储结构
每个节点使用指针来指向其子节点,这种方式灵活简单,适用于不确定树的大小或结构的情况。每个节点通常包含一个数据域和多个指针域(指向子节点的指针)。
#include <stdio.h>
#include <stdlib.h>
// 定义树的节点结构
typedef struct TreeNode {
int data; // 节点数据
struct TreeNode *left; // 左子树指针
struct TreeNode *right; // 右子树指针
} TreeNode;
// 创建新节点
TreeNode* createNode(int data) {
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
if (newNode == NULL) {
printf("内存分配失败\n");
exit(1);
}
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
// 打印树形结构
void printTree(TreeNode* root, int depth) {
if (root == NULL) {
return;
}
// 递归打印右子树
printTree(root->right, depth + 1);
// 打印当前节点
for (int i = 0; i < depth; i++) {
printf(" ");
}
printf("%d\n", root->data);
// 递归打印左子树
printTree(root->left, depth + 1);
}
int main() {
// 创建根节点
TreeNode* root = createNode(5);
// 构建示例树结构
root->left = createNode(3);
root->right = createNode(8);
root->left->left = createNode(1);
root->left->right = createNode(4);
root->right->left = createNode(7);
root->right->right = createNode(9);
// 打印树形结构
printf("树的结构:\n");
printTree(root, 0);
return 0;
}
4.2 数组存储结构
将树的节点按照某种顺序(如层序遍历)依次存储在数组中,通过数组的下标关系来表示节点之间的父子关系。这种方式节省了指针的空间,但需要事先确定树的最大层数和每层节点的最大数量。
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
// 定义树的节点结构
typedef struct TreeNode {
int data; // 节点数据
} TreeNode;
// 创建新节点
TreeNode* createNode(int data) {
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
if (newNode == NULL) {
printf("内存分配失败\n");
exit(1);
}
newNode->data = data;
return newNode;
}
// 初始化树的数组
void initTree(TreeNode* tree[], int size) {
for (int i = 0; i < size; i++) {
tree[i] = NULL;
}
}
// 插入节点
void insert(TreeNode* tree[], int data, int index) {
if (index >= MAX_SIZE) {
printf("数组越界\n");
return;
}
tree[index] = createNode(data);
}
// 层次遍历打印树的结构
void printTree(TreeNode* tree[], int size) {
int i = 0;
int level = 1;
int nodesInLevel = 1;
while (i < size) {
printf("%d ", tree[i]->data);
if (++i == nodesInLevel) {
printf("\n");
nodesInLevel = nodesInLevel * 2 + 1;
level++;
}
}
printf("\n");
}
int main() {
// 初始化树的数组
TreeNode* tree[MAX_SIZE];
initTree(tree, MAX_SIZE);
// 插入节点
insert(tree, 5, 0);
insert(tree, 3, 1);
insert(tree, 8, 2);
insert(tree, 1, 3);
insert(tree, 4, 4);
insert(tree, 7, 5);
insert(tree, 9, 6);
// 打印树的结构
printf("树的结构:\n");
printTree(tree, 7);
return 0;
}
4.3 父节点指针存储结构
每个节点除了存储自身的数据外,还存储指向父节点的指针。这种方式常用于需要在树中向上移动的操作,如查找节点的父节点。
#include <stdio.h>
#include <stdlib.h>
// 定义树的节点结构
typedef struct TreeNode {
int data; // 节点数据
struct TreeNode *left; // 左子树指针
struct TreeNode *right; // 右子树指针
struct TreeNode *parent; // 父节点指针
} TreeNode;
// 创建新节点
TreeNode* createNode(int data) {
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
if (newNode == NULL) {
printf("内存分配失败\n");
exit(1);
}
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
newNode->parent = NULL;
return newNode;
}
// 插入节点
void insert(TreeNode** root, int data, TreeNode* parent) {
if (*root == NULL) {
*root = createNode(data);
(*root)->parent = parent;
} else {
if (data < (*root)->data) {
insert(&((*root)->left), data, *root);
} else {
insert(&((*root)->right), data, *root);
}
}
}
// 打印树形结构
void printTree(TreeNode* root, int level) {
if (root == NULL) {
return;
}
printTree(root->right, level + 1);
for (int i = 0; i < level; i++) {
printf(" ");
}
printf("%d\n", root->data);
printTree(root->left, level + 1);
}
int main() {
// 创建根节点
TreeNode* root = NULL;
// 插入节点
insert(&root, 5, NULL);
insert(&root, 3, root);
insert(&root, 8, root);
insert(&root, 1, root->left);
insert(&root, 4, root->left);
insert(&root, 7, root->right);
insert(&root, 9, root->right);
// 打印树形结构
printf("树的结构:\n");
printTree(root, 0);
return 0;
}
4.4 孩子兄弟表示法
也称为二叉树的二叉链表存储结构,每个节点包含两个指针,分别指向其第一个子节点和其兄弟节点。这种方式常用于表示多叉树,如二叉树的拓展形式。
#include <stdio.h>
#include <stdlib.h>
// 定义树的节点结构
typedef struct TreeNode {
int data; // 节点数据
struct TreeNode *firstChild; // 第一个孩子节点指针
struct TreeNode *nextSibling; // 兄弟节点指针
} TreeNode;
// 创建新节点
TreeNode* createNode(int data) {
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
if (newNode == NULL) {
printf("内存分配失败\n");
exit(1);
}
newNode->data = data;
newNode->firstChild = NULL;
newNode->nextSibling = NULL;
return newNode;
}
// 打印树形结构
void printTree(TreeNode* root, int level) {
if (root == NULL) {
return;
}
// 打印当前节点
printf("%d ", root->data);
// 递归打印孩子节点
if (root->firstChild != NULL) {
printTree(root->firstChild, level + 1);
}
// 递归打印兄弟节点
if (root->nextSibling != NULL) {
printf("\n"); // 换行
for (int i = 0; i < level; i++) {
printf(" "); // 打印缩进
}
printTree(root->nextSibling, level);
}
}
int main() {
// 创建根节点
TreeNode* root = createNode(5);
// 构建示例树结构
root->firstChild = createNode(3);
root->firstChild->nextSibling = createNode(8);
root->firstChild->firstChild = createNode(1);
root->firstChild->firstChild->nextSibling = createNode(4);
root->firstChild->nextSibling->firstChild = createNode(7);
root->firstChild->nextSibling->firstChild->nextSibling = createNode(9);
// 打印树形结构
printf("树的结构:\n");
printTree(root, 0);
printf("\n"); // 添加额外的换行
return 0;
}