二叉树性质 |
---|
性质1: 在二叉树的第i层上至多有2i-1个结点(i>0) |
性质2: 深度为k的二叉树至多有2k-1个结点(k>0) |
性质3: 对于任何一棵二叉树,若2度的结点数有n2个,则叶子数(n0)必定为n2+1 (即n0=n2+1) |
满二叉树:一棵深度为k 且有2k -1个结点的二叉树。 (特点:每层都“充满”了结点) |
1 树的表示法
1 二叉链
// 二叉链表示法
/*
typedef struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/
struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
void main01() {
BiTNode t1, t2, t3, t4, t5;
t1.data = 1;
t2.data = 2;
t3.data = 3;
t4.data = 4;
t5.data = 5;
// 建立关系
t1.lchild = &t2;
t1.rchild = &t3;
t2.lchild = &t4;
t3.lchild = &t5;
// 树的遍历
}
void main02() {
BiTNode* p1, * p2, * p3, * p4, * p5;
p1 = (BiTNode*)malloc(sizeof(BiTNode));
p2 = (BiTNode*)malloc(sizeof(BiTNode));
p3 = (BiTNode*)malloc(sizeof(BiTNode));
p4 = (BiTNode*)malloc(sizeof(BiTNode));
p5 = (BiTNode*)malloc(sizeof(BiTNode));
p1->data = 1;
p2->data = 2;
p3->data = 3;
p4->data = 4;
p5->data = 5;
// 建立关系
p1->lchild = p2;
p1->rchild = p3;
p2->lchild = p4;
p3->lchild = p5;
// 树的遍历
}
2 三叉链
// 三叉链表
typedef struct TriTNode {
int data;
struct TriTNode* lchild, * rchild;
struct TriTNode* parent;
}TriTNode, *TriTree;
3 双亲链表
// 双亲链表
#define MAX_TREE_SIZE 100
typedef struct BPTNode {
int data;
int parentPosition; // 指向双亲的指针 // 数组下标
char LRTag; // 左右孩子标志域
}BPTNode;
typedef struct BPTree {
BPTNode nodes[100];//因为节点之间是分散的,需要把节点存储到数组中
int num_node; // 节点数目
int root; // 根节点的位置// 注意此域存储的是父亲节点在数组的下标
}BPTree;
void main() {
BPTree tree;
// 根节点
tree.nodes[0].parentPosition = 1000;
// B节点
tree.nodes[1].parentPosition = 0;
tree.nodes[1].data = 'B';
tree.nodes[1].LRTag = 1;
// C节点
tree.nodes[2].parentPosition = 0;
tree.nodes[2].data = 'C';
tree.nodes[2].LRTag = 2;
}
2 树的遍历
1 先序遍历
先遍历根,再遍历左子树,再遍历右子树
2 中序遍历
先遍历左子树,再遍历根,再遍历右子树
3 后序遍历
先遍历左子树,再遍历右子树,再遍历根
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tHzRJHoh-1632112771959)(C:\Users\302\AppData\Roaming\Typora\typora-user-images\image-20210907101926430.png)]
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
// 二叉链表示法
/*
typedef struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/
struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
void preOreder(BiTNode *root) {
if (root == NULL) {
return;
}
printf("%d ", root->data);
// 遍历左子树
preOreder(root->lchild);
// 遍历右子树
preOreder(root->rchild);
}
void midOreder(BiTNode* root) {
if (root == NULL) {
return;
}
// 遍历左子树
midOreder(root->lchild);
printf("%d ", root->data);
// 遍历右子树
midOreder(root->rchild);
}
void rearOreder(BiTNode* root) {
if (root == NULL) {
return;
}
// 遍历左子树
rearOreder(root->lchild);
// 遍历右子树
rearOreder(root->rchild);
printf("%d ", root->data);
}
void main() {
BiTNode t1, t2, t3, t4, t5;
memset(&t1, 0, sizeof(BiTNode));
memset(&t2, 0, sizeof(BiTNode));
memset(&t3, 0, sizeof(BiTNode));
memset(&t4, 0, sizeof(BiTNode));
memset(&t5, 0, sizeof(BiTNode));
t1.data = 1;
t2.data = 2;
t3.data = 3;
t4.data = 4;
t5.data = 5;
// 建立关系
t1.lchild = &t2;
t1.rchild = &t3;
t2.lchild = &t4;
t3.lchild = &t5;
// 树的遍历
printf("先序遍历:\n");
preOreder(&t1);
printf("\n");
printf("中序遍历:\n");
midOreder(&t1);
printf("\n");
printf("后序遍历:\n");
rearOreder(&t1);
}
结果
先序遍历:
1 2 4 3 5
中序遍历:
4 2 1 5 3
后序遍历:
4 2 5 3 1
4 树的非递归遍历(中序遍历)
栈模型
C++版本
#include<iostream>
using namespace std;
#include<stack>
/*
步骤1:
如果结点有左子树,该结点入栈;
如果结点没有左子树,访问该结点;
步骤2:
如果结点有右子树,重复步骤1;
如果结点没有右子树(结点访问完毕),根据栈顶指示回退,访问栈顶元素,并访问右子树,重复步骤1
如果栈为空,表示遍历结束。
*/
typedef struct BiTNode {
int data;
struct BiTNode* lchild;
struct BiTNode* rchild;
}BiTNode,*BiTree;
// 找到中序遍历起点
BiTNode *goLeft(BiTNode* T, stack<BiTNode*>& s) {
if (T == NULL) {
return NULL;
}
// 判断T有无左子树,没有返回,若有继续
while (T->lchild != NULL) {
s.push(T);
T = T->lchild;
}
return T;
}
void InOrder(BiTNode* T) {
//
BiTNode* t = NULL;
stack<BiTNode*> s;
t = goLeft(T, s);
while (t) {
printf("%d ", t->data);
//如果t有右子树 重复步骤1
if (t->rchild != NULL) {
t = goLeft(t->rchild, s); // 右子树中中序遍历起点
}
// 如果t没有右子树 根据栈顶指示 回退
else if (!s.empty()) {
t = s.top();
s.pop();
}
// 如果没有右子树且栈为空
else {
t = NULL;
}
}
}
void midOrder(BiTNode* T) {
if (T == NULL) {
return;
}
// 遍历左子树
midOrder(T->lchild);
printf("%d ", T->data);
// 遍历右子树
midOrder(T->rchild);
}
void main() {
BiTNode t1, t2, t3, t4, t5;
memset(&t1, 0, sizeof(BiTNode));
memset(&t2, 0, sizeof(BiTNode));
memset(&t3, 0, sizeof(BiTNode));
memset(&t4, 0, sizeof(BiTNode));
memset(&t5, 0, sizeof(BiTNode));
t1.data = 1;
t2.data = 2;
t3.data = 3;
t4.data = 4;
t5.data = 5;
t1.lchild = &t2;
t1.rchild = &t3;
t2.lchild = &t4;
t3.lchild = &t5;
printf("中序遍历:\n");
midOrder(&t1);
cout << endl;
cout << "中序遍历(非递归):\n";
InOrder(&t1);
cout << endl;
}
结果
中序遍历:
4 2 1 5 3
中序遍历(非递归):
4 2 1 5 3
C语言版本
#include<stdio.h>
#include<stdlib.h>
#include"linkstack.h"
/*
步骤1:
如果结点有左子树,该结点入栈;
如果结点没有左子树,访问该结点;
步骤2:
如果结点有右子树,重复步骤1;
如果结点没有右子树(结点访问完毕),根据栈顶指示回退,访问栈顶元素,并访问右子树,重复步骤1
如果栈为空,表示遍历结束。
*/
typedef struct BiTNode {
int data;
struct BiTNode* lchild;
struct BiTNode* rchild;
}BiTNode, * BiTree;
// 找到中序遍历起点
BiTNode* goLeft(BiTNode* T, LinkStack* s) {
if (T == NULL) {
return NULL;
}
// 判断T有无左子树,没有返回,若有继续
while (T->lchild != NULL) {
LinkStack_Push(s, (void*)T);
T = T->lchild;
}
return T;
}
void InOrder(BiTNode* T) {
//
BiTNode* t = NULL;
//stack<BiTNode*> s;
LinkStack* s = LinkStack_Create();
t = goLeft(T, s);
while (t) {
printf("%d ", t->data);
//如果t有右子树 重复步骤1
if (t->rchild != NULL) {
t = goLeft(t->rchild, s); // 右子树中中序遍历起点
}
// 如果t没有右子树 根据栈顶指示 回退
//else if (!s.empty()) {
else if(LinkStack_Size(s)>0){
/*t = s.top();
s.pop();*/
t = (BiTNode*)LinkStack_Pop(s);
}
// 如果没有右子树且栈为空
else {
t = NULL;
}
}
}
void midOrder(BiTNode* T) {
if (T == NULL) {
return;
}
// 遍历左子树
midOrder(T->lchild);
printf("%d ", T->data);
// 遍历右子树
midOrder(T->rchild);
}
void main() {
BiTNode t1, t2, t3, t4, t5;
memset(&t1, 0, sizeof(BiTNode));
memset(&t2, 0, sizeof(BiTNode));
memset(&t3, 0, sizeof(BiTNode));
memset(&t4, 0, sizeof(BiTNode));
memset(&t5, 0, sizeof(BiTNode));
t1.data = 1;
t2.data = 2;
t3.data = 3;
t4.data = 4;
t5.data = 5;
t1.lchild = &t2;
t1.rchild = &t3;
t2.lchild = &t4;
t3.lchild = &t5;
printf("中序遍历:\n");
midOrder(&t1);
printf("\n");
printf("中序遍历(非递归):\n");
InOrder(&t1);
printf("\n");
}
结果
中序遍历:
4 2 1 5 3
中序遍历(非递归):
4 2 1 5 3
3 #法创建树
#define _CRT_SECURE_NO_WARNINGS
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
// 二叉链表示法
/*
typedef struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/
struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
void InOrder(BiTNode* T) {
if (T == NULL) {
return;
}
InOrder(T->lchild);
printf("%d ", T->data);
InOrder(T->rchild);
}
// 销毁树时用后续方法销毁
void FreeTree(BiTNode* T) {
if (T == NULL) {
return;
}
if (T->lchild != NULL) {
FreeTree(T->lchild);
T->lchild = NULL;
}
if (T->rchild != NULL) {
FreeTree(T->rchild);
T->rchild = NULL;
}
if (T != NULL) {
free(T);
T = NULL;
}
}
// #法创建树 先序方法建立二叉链表
BiTNode* BiTree_Create() {
BiTNode* tmp = NULL;
char ch;
scanf("%c", &ch);
if (ch == '#') {
return NULL;
}
else {
tmp = (BiTNode*)malloc(sizeof(BiTNode));
if (tmp == NULL) {
return NULL;
}
tmp->data = ch; //生成节点
tmp->lchild = BiTree_Create();
tmp->rchild = BiTree_Create();
}
return tmp;
}
void main() {
int nCount = 0, depthval = 0;
BiTNode* my = BiTree_Create();
printf("\n 中序遍历 \n");
InOrder(my);
printf("\n");
FreeTree(my);
}
结果
1#23###
中序遍历
49 51 50
4 二叉线索树
希望可以像链表一样能够遍历树
结论:线索化过程就是在遍历过程(假设是中序遍历)中修改空指针的过程:
将空的lchild改为结点的直接前驱;
将空的rchild改为结点的直接后继。
可以把二叉树中序遍历结果存到链表里,再进行遍历,实现同样的效果
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define OK 1
#define ERROR -1
#define OVERFLOW 0
typedef enum{Link, Thread} PointerTag;
typedef char TElemType;
typedef int Status;
typedef struct _tag_BiThrNode {
TElemType data;
struct _tag_BiThrNode* lchild, * rchild;
PointerTag LTag;
PointerTag RTag;
}BiThrNode, * BiThrTree;
TElemType Nil = '#';
Status visit(TElemType e) {
printf("%c", e);
return OK;
}
// 按前序输入二叉线索树中节点的值,构造二叉 线索树T
// 0(整型)/空格(字符型)表示空节点
// 1##
Status CreateBiThrTree(BiThrTree* T) {
TElemType h;
scanf("%c", &h);
if (h == Nil)
* T = NULL;
else {
*T = (BiThrTree)malloc(sizeof(BiThrNode));
if (!*T)
exit(OVERFLOW);
(*T)->data = h; //生成根节点(前序)
CreateBiThrTree(&(*T)->lchild); //递归构造左子树
if ((*T)->lchild) //有左孩子
(*T)->LTag = Link;
CreateBiThrTree(&(*T)->rchild); //递归构造右子树
if ((*T)->rchild) //有有孩子
(*T)->RTag = Link;
}
return OK;
}
BiThrTree pre; // 全局变量,始终指向刚刚访问过的节点
// 中序遍历进行中序线索化
void InThreading(BiThrTree p) {
if (p) {
InThreading(p->lchild); // 递归左子树线索化
if (!p->lchild) // 没有左孩子
{
p->LTag = Thread; //前驱线索
p->lchild = pre; //左孩子指针指向前驱
}
if (!pre->rchild) // 前驱没有右孩子
{
pre->RTag = Thread; //后驱线索
pre->rchild = p; //前驱右孩子指针指向后驱(当前节点p)
}
pre = p;//保持pre指向p的前驱
InThreading(p->rchild);//递归右子树线索化
}
}
/* 中序遍历二叉树T,并将其中序线索化,Thrt指向头节点*/
Status InOrderThreading(BiThrTree* Thrt, BiThrTree T) {
*Thrt = (BiThrTree)malloc(sizeof(BiThrNode));
if (!*Thrt)
exit(OVERFLOW);
(*Thrt)->LTag = Link;//建头节点
(*Thrt)->RTag = Thread;
(*Thrt)->rchild = (*Thrt); //右指针回指
if (!T)//若二叉树为空,则左指针回指
(*Thrt)->lchild = *Thrt;
else {
(*Thrt)->lchild = T;
pre = (*Thrt);
InThreading(T); //中序遍历进行中序线索化
pre->rchild = *Thrt;
pre->RTag = Thread;//最后一个节点线索化
(*Thrt)->rchild = pre;
}
return OK;
}
// 中序遍历二叉线索树T(头节点)的非递归算法
Status InOrderTraverse_Thr(BiThrTree T) {
BiThrTree p;
p = T->lchild; // p指向根节点
while (p != T) {
// 空树或遍历结束 p==T
while (p->LTag == Link)p = p->lchild;
if (!visit(p->data)) //访问其左子树为空的节点
return ERROR;
while (p->RTag == Thread && p->rchild != T) {
p = p->rchild;
visit(p->data); //访问后继节点
}
p = p->rchild;
}
return OK;
}
int main() {
BiThrTree H, T;
printf("请按先序输入二叉树(如'ABDH##I##EJ###CF##G##')");
CreateBiThrTree(&T); //按先序产生二叉树
InOrderThreading(&H, T); // 中序遍历;并中序线索化二叉树
printf("中序遍历(输出)二叉线索树:\n");
InOrderTraverse_Thr(H); //中序遍历输出二叉线索树
printf("\n");
}
结果
请按先序输入二叉树(如'ABDH##I##EJ###CF##G##')ABDH##I##EJ###CF##G##
中序遍历(输出)二叉线索树:
HDIBJEAFCG
5 霍夫曼树
霍夫曼树
1.给定n个数值{ v1, v2, …, vn}
2.根据这n个数值构造二叉树集合F
F = { T1, T2, …, Tn}
Ti的数据域为vi,左右子树为空
3.在F中选取两棵根结点的值最小的树作为左右子树构造一棵新的二叉树,这棵二叉树的根结点中的值为左右子树根结点中的值之和
4.在F中删除这两棵子树,并将构造的新二叉树加入F中
5.重复3和4,直到F中只剩下一个树为止。这棵树即霍夫曼树
霍夫曼树是一种特殊的二叉树
霍夫曼树应用于信息编码和数据压缩领域
霍夫曼树是现代压缩算法的基础
案例1 计算叶子节点的个数
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
// 二叉链表示法
/*
typedef struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/
struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
int sum = 0;
void coutLeaf(BiTNode* T) {
if (T != NULL) {
if (T->lchild == NULL && T->rchild == NULL) {
sum++;
}
if (T->lchild) {
coutLeaf(T->lchild);
}
if (T->rchild) {
coutLeaf(T->rchild);
}
}
}
// 采用指针的方式,不使用全局变量
void coutLeaf2(BiTNode* T, int *sum) {
// 采用前序求叶子节点,中序求叶子节点,后序求叶子节点结果一样
if (T != NULL) {
if (T->lchild == NULL && T->rchild == NULL) {
(*sum)++; // ++的优先级高
}
if (T->lchild) {
coutLeaf2(T->lchild, sum);
}
/*
if (T->lchild == NULL && T->rchild == NULL) {
(*sum)++; // ++的优先级高
}
*/
if (T->rchild) {
coutLeaf2(T->rchild, sum);
}
/*
if (T->lchild == NULL && T->rchild == NULL) {
(*sum)++; // ++的优先级高
}
*/
}
}
void main() {
BiTNode t1, t2, t3, t4, t5;
memset(&t1, 0, sizeof(BiTNode));
memset(&t2, 0, sizeof(BiTNode));
memset(&t3, 0, sizeof(BiTNode));
memset(&t4, 0, sizeof(BiTNode));
memset(&t5, 0, sizeof(BiTNode));
t1.data = 1;
t2.data = 2;
t3.data = 3;
t4.data = 4;
t5.data = 5;
// 建立关系
t1.lchild = &t2;
t1.rchild = &t3;
t2.lchild = &t4;
t3.lchild = &t5;
coutLeaf(&t1);
printf("sum:%d \n", sum);
{
int mysum = 0;
coutLeaf2(&t1, &mysum);
printf("sum:%d \n", mysum);
}
}
结果
sum:2
sum:2
案例2 求树的深度
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
// 二叉链表示法
/*
typedef struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/
struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
int Depth(BiTNode *T) {
int depthleft = 0;
int depthright = 0;
int depthval = 0;
if (T == NULL) {
depthval = 0;
return depthval;
}
// 求左子树的高度
depthleft = Depth(T->lchild);
// 求右子树的高度
depthright = Depth(T->rchild);
// +1
depthval = 1 + (depthleft > depthright ? depthleft : depthright);
return depthval;
}
void main() {
BiTNode t1, t2, t3, t4, t5;
memset(&t1, 0, sizeof(BiTNode));
memset(&t2, 0, sizeof(BiTNode));
memset(&t3, 0, sizeof(BiTNode));
memset(&t4, 0, sizeof(BiTNode));
memset(&t5, 0, sizeof(BiTNode));
t1.data = 1;
t2.data = 2;
t3.data = 3;
t4.data = 4;
t5.data = 5;
// 建立关系
t1.lchild = &t2;
t1.rchild = &t3;
t2.lchild = &t4;
t3.lchild = &t5;
printf("%d \n",Depth(&t1));
}
结果
3
案例3 copy Tree
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
// 二叉链表示法
/*
typedef struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/
struct BiTNode {
int data;
struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
void midOreder(BiTNode* root) {
if (root == NULL) {
return;
}
// 遍历左子树
midOreder(root->lchild);
printf("%d ", root->data);
// 遍历右子树
midOreder(root->rchild);
}
BiTNode* CopyTree(BiTNode *T) {
BiTNode* newNode = NULL;
BiTNode* newLp = NULL;
BiTNode* newRp = NULL;
if (T == NULL) {
return NULL;
}
// copy 左子树
if (T->lchild != NULL) {
newLp = CopyTree(T->lchild); // copy左子树
}
else {
newLp = NULL;
}
// copy 右子树
if (T->rchild != NULL) {
newRp = CopyTree(T->rchild);
}
else {
newRp = NULL;
}
// malloc根节点
newNode = (BiTNode*)malloc(sizeof(BiTNode));
if (newNode == NULL) {
return NULL;
}
newNode->lchild = newLp;
newNode->rchild = newRp;
newNode->data = T->data;
return newNode;
}
void main() {
BiTNode t1, t2, t3, t4, t5;
memset(&t1, 0, sizeof(BiTNode));
memset(&t2, 0, sizeof(BiTNode));
memset(&t3, 0, sizeof(BiTNode));
memset(&t4, 0, sizeof(BiTNode));
memset(&t5, 0, sizeof(BiTNode));
t1.data = 1;
t2.data = 2;
t3.data = 3;
t4.data = 4;
t5.data = 5;
// 建立关系
t1.lchild = &t2;
t1.rchild = &t3;
t2.lchild = &t4;
t3.lchild = &t5;
printf("midOrder:\n");
midOreder(&t1);
printf("\n");
{
BiTNode* root = CopyTree(&t1);
printf("copy midOrder:\n");
midOreder(root);
printf("\n");
}
}
结果
midOrder:
4 2 1 5 3
copy midOrder:
4 2 1 5 3
确定树的方法:通过中序遍历和先序遍历可以确定一个树
通过中序遍历和后序遍历可以确定一个树