1. 定义
-
如何理解节点、边?
节点是集合,边是关系(根节点是全集) -
链表看作“一叉树”,结构定义中表示关系的指针扩展为指针数组,就变成了树的结构定义
-
二进制可以表示所有进制,二叉树可以表示所有树形结构(左孩子右兄弟,又称十字链表法)
-
二叉树性质:
节点数量等于边数+1 --> 度为0的节点比度为2的节点多1个 -
二叉树中,若除掉最大阶层后为满二叉树,且最大阶层节点均向左靠齐,则称为完全二叉树
-
二叉树性质
性质1:二叉树的第 i 层最多有 2i-1 个节点
性质2:深度为 k 的二叉树最多有 2k-1 个节点
性质3:二叉树终端节点个数为 n0,度为2的节点个数为 n2,则 n0=n2+1
性质4:具有 n 个节点的完全二叉树的深度为 [log2n]+1 -
完全二叉树可以通过顺序结构(数组)实现,关系通过计算得到,不同于指针的记录方式
-
广义表是用字符串存储表示二叉树(节点和关系),实现二叉树的共享
【理解】函数的判断一般是对传入参数的合法性的判断
2. 代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct Node {
int data;
struct Node *lchild, *rchild;
} Node;
typedef struct Tree {
Node *root;//指向二叉树根结点的指针变量
int n;//二叉树的结点个数
} Tree;
//结构操作:结点的初始化和树的初始化
Node *getNewNode(int val) {//val封装成二叉树的结点
//指向结点的指针变量p,记录malloc申请空间的首地址
Node *p = (Node *)malloc(sizeof(Node));
//初始化p的字段
p->data = val;
p->lchild = p->rchild = NULL;
return p;
}
Tree *getNewTree() {
//指向该二叉树的指针变量tree,记录malloc开辟的树的首地址
Tree *tree = (Tree *)malloc(sizeof(Tree));
//初始化tree的字段
tree->root = NULL;
tree->n = 0;
return tree;
}
//清空结点和二叉树
void clearNode(Node *node) {
if (node == NULL) return ;
//递归回收孩子结点
clearNode(node->lchild);
clearNode(node->rchild);
free(node);
return ;
}
void clear(Tree *tree) {
if (tree == NULL) return ;
//调用clearNode沿着根结点去递归回收
clearNode(tree->root);
free(tree);
return ;
}
//insert_node返回当前二叉树的根结点
Node *insert_node(Node *root, int val, int *flag) {
//定义flag指针变量
if (root == NULL) {
*flag = 1;//通过不断地递归调用,root为空时val成功插入
return getNewNode(val);//root为空,调用getNewNode把val封装成一个结点,就是当前树的根结点
}
//根结点的值等于待插入的val,什么也不做,返回root
if (root->data == val) return root;
//val<root->data时,向当前根结点的左子树递归插入
//【递归】:先判断插入位置,递归调用insert_node,插入位置为空时才能插入
if (val < root->data) root->lchild = insert_node(root->lchild, val, flag);
else root->rchild = insert_node(root->rchild, val, flag);
return root;
}
//二叉排序树(二叉查找树)的性质:对于任何一个三元组,左孩子值<根结点值<右孩子值
//向当前tree插入一个值为val的结点
void insert(Tree *tree, int val){
int flag = 0;//在tree中判断val成功插入与否
//注意&,flag是传出参数,执行完insert_node后传回来
tree->root = insert_node(tree->root, val, &flag);
tree->n += flag;//插入成功+1,失败+0
return ;
}
//前序遍历
void pre_order_node(Node *node) {
//传进来结点的地址
if (node == NULL) return ;
printf("%d ", node->data);
pre_order_node(node->lchild);
pre_order_node(node->rchild);
return ;
}
void pre_order(Tree *tree) {//传进来一棵树
if (tree == NULL) return ;
printf("pre_order : ");
pre_order_node(tree->root);//沿着根结点进行前序遍历
printf("\n");
return ;
}
//中序遍历,对于二叉排序树而言打印出来是从小到大排序
void in_order_node(Node *node) {
//传进来结点的地址
if (node == NULL) return ;
in_order_node(node->lchild);
//打印根结点的值放在中间
printf("%d ", node->data);
in_order_node(node->rchild);
return ;
}
void in_order(Tree *tree) {//传进来一棵树
if (tree == NULL) return ;
printf("in_order : ");
in_order_node(tree->root);//沿着根结点进行前序遍历
printf("\n");
return ;
}
//后序遍历
void post_order_node(Node *node) {
//传进来结点的地址
if (node == NULL) return ;
post_order_node(node->lchild);
post_order_node(node->rchild);
//后序遍历打印根结点的值放在最后
printf("%d ", node->data);
return ;
}
void post_order(Tree *tree) {//传进来一棵树
if (tree == NULL) return ;
printf("post_order : ");
post_order_node(tree->root);//沿着根结点进行前序遍历
printf("\n");
return ;
}
void output_node(Node *root) {
if (root == NULL) return ;
//先打印根结点的值
printf("%d", root->data);
if (root->lchild == NULL && root->rchild == NULL) return ;
//先考虑输出数据,后面补充括号和逗号的输出
printf("(");
output_node(root->lchild);
printf(",");
output_node(root->rchild);
printf(")");
return ;
}
//输出二叉树的广义表表示方法
void output(Tree *tree) {
if (tree == NULL) return ;
printf("tree(%d) : ", tree->n);
output_node(tree->root);
printf("\n");
return ;
}
int main() {
srand(time(0));
//初始化一棵二叉树
Tree *tree = getNewTree();
#define max_op 10
for (int i = 0; i < max_op; i++) {
int val = rand() % 100;
insert(tree, val);
output(tree);
}
pre_order(tree);
in_order(tree);
post_order(tree);
#undef max_op
clear(tree);
return 0;
}