目录
二叉树
二叉树:每个结点最多有2个子结点
二叉树的代码实现
结构体
typedef int BTElemType;
typedef struct BiTreeNode {
BTElemType data;
BiTreeNode* lChild;
BiTreeNode* rChild;
}*BiTree;
初始化
由于很多语句都需要判断结点存不存在,因此二叉树的初始化就是T=NULL
相似的,之后在创建树时,每个结点的左右孩子一开始也需要=NULL
保证结点被声明时=NULL,左右孩子被赋值之前=NULL,直到有值赋给他们,否则指针会变成野指针
//初始化
void InitBT(BiTree& T) {
T = NULL;
}
//判树空(无任何结点)
bool BTIsEmpty(BiTree T) {
if (!T)
return true;
else
return false;
}
前序创建树
理解:
1.将二叉树T传入函数,首先输入一个元素e
2.若e非0,T->data=e; T->lChild=NULL; T->rChild=NULL;
3. T的左右孩子先后调用此函数,不断创建
//前序创建树(输入非0值)
void CreatBT(BiTree& T) {
BTElemType e;
cin >> e;
InitBT(T);
if (e!=0) {
T = (BiTree)malloc(sizeof(BiTreeNode));
if (!T)
exit(OVERFLOW);
T->data = e;
T->lChild = NULL;
T->rChild = NULL;
CreatBT(T->lChild);
CreatBT(T->rChild);
}
}
二叉树的遍历(前中后、层序遍历)
前中后序遍历的区别即是 访问左子树、右子树、根节点的顺序不同
前序遍历:传进结点->输出data->左孩子递归->右孩子递归
中序遍历:相当于一直找到左孩子不存在的结点,输出本结点data,再右孩子递归
后序遍历:从左子树开始找,找到底层(左右孩子都为NULL了),输出那个结点,再向上回溯
//前序遍历
void PreOrderTraverse(BiTree T) {
if (T) {
cout << T->data << " ";
PreOrderTraverse(T->lChild);
PreOrderTraverse(T->rChild);
}
}
//中序遍历
void InOrderTraverse(BiTree T) {
if (T) {
InOrderTraverse(T->lChild);
cout << T->data << " ";
InOrderTraverse(T->rChild);
}
}
//后序遍历
void NextOrderTraverse(BiTree T) {
if (T) {
NextOrderTraverse(T->lChild);
NextOrderTraverse(T->rChild);
cout << T->data << " ";
}
}
层序遍历:用队列,存储每一层的结点
从根节点开始入队,只要队不空,出队,输出data,再将当前节点的左右孩子入队
//层序遍历
void LevelOrderTraverse(BiTree T) {
LinkQueue Q;
BiTree p; //存储出队的
if (T) {
InitLQ(Q);
EnLQ(Q, (LQElemType)T);
while (!LQIsEmpty(Q)) {
DeLQ(Q, (LQElemType&)p);
cout << p->data << " ";
if (p->lChild)
EnLQ(Q, (LQElemType)p->lChild);
if (p->rChild)
EnLQ(Q, (LQElemType)p->rChild);
}
}
}
求树的深度
//求树的深度
int BTDepth(BiTree T) {
int lDepth, rDepth;
if (!T)
return 0;
lDepth = BTDepth(T->lChild);
rDepth = BTDepth(T->rChild);
if (lDepth >= rDepth)
return lDepth + 1;
else
return rDepth + 1;
}
理解!!!
(1)如果一颗树只有一个结点,它的深度是 1;
(2)如果根结点只有左子树而没有右子树,那么二叉树的深度应该是其左子树的深度加 1;
(3)如果根结点只有右子树而没有左子树,那么二叉树的深度应该是其右树的深度加 1;
(4)如果根结点既有左子树又有右子树,那么二叉树的深度应该是其左右子树的深度较大值加 1
——>
如果这个结点为空,它对深度的贡献就是0,返回给上一层;
向上回溯,每层贡献的深度是下一层返回的深度+1(这个1是本结点贡献的深度)
向上回溯过程中,返回的是lDepth和rDepth中的较大值,因为——根节点到叶结点的最长路径为二叉树的深度
哈夫曼树
基本概念
1、权
2、路径:从树中的一个结点到另一个结点
1)路径长度:路径上的分支数
2)树的路径长度:从树根到每个结点的路径长度之和
3)结点的带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积
4)树的带权路径长度:树中所有带权结点的路径长度之和
3、哈夫曼树:带权路径长度最小的树
哈夫曼树的代码实现
一.哈夫曼树的构成
一棵有n个叶子结点的哈夫曼树中,度为2的结点有n-1个,则共有2n-1个结点,可以用大小为2n-1的一维数组来存储
每个数组元素是这样的结构体:
结构体
typedef struct HuffmanTreeNode {
int lChild, rChlid, parent;
int weight;
}*HuffmanTree;
二、创建哈夫曼树
1.初始化
1)为HT开辟2n-1(total)个存储单元
2)初始化每个单元,0~n个单元,左右孩子、双亲初始化为-1(由于-1不存在,所以实际上的意思是初始化的时候先都标记为无),第 i 个的权值为传进来的数组元素weight[i]
n~total个单元,左右孩子、双亲初始化为-1,第 i 个的权值为0(也是初始化思想)
2.创建树
n~total是新添加的数,在n~total循环,把其前边最小的数和第二小的数变成它的左右孩子下标,左右孩子的parent下标变成它的下标,它的权值为左右孩子之和
//返回前k个最小的权值
int Min(HuffmanTree HT, int k) {
int i = 0;
int min;
while (HT[i].parent != -1) //从下标0找到第一个没有父结点的,从它开始找
i++;
min = i; //将其暂时赋值给min
//i到k找到最小权值的下标
for (; i < k; i++)
//如果一样,则返回前面的
if (HT[i].weight < HT[min].weight && HT[i].parent == -1)
min = i;
HT[min].parent = 1; //改变parent的值,下次就不找这个点了
return min;
}
void CreatHuffmanTree(HuffmanTree& HT, int* weight, int n) {
int total = 2 * n - 1; //remember
HT = new HuffmanTreeNode[total]; //给传进来的赫夫曼树分配空间,一共有total个结构体
if (!HT)
exit(OVERFLOW);
//给每个结构体初始化
for (int i = 0; i < n; i++) {
HT[i].lChild = HT[i].rChlid = HT[i].parent = -1;
HT[i].weight = weight[i];
}
for (int i = n; i < total; i++) {
HT[i].lChild = HT[i].rChlid = HT[i].parent = -1;
HT[i].weight = 0;
}
for (int i = n; i < total; i++) {
//先找的min1,所以min1一定是小于min2的,它在前边
int min1 = Min(HT, i);
int min2 = Min(HT, i);
HT[min1].parent = HT[min2].parent = i;
//给HT[i]的各种成员赋值
HT[i].weight = HT[min1].weight + HT[min2].weight;
HT[i].lChild = min1;
HT[i].rChlid = min2;
}
}
三.为哈夫曼树编码
1、基本思想:概率大的字母用短码,小的用长码,构造哈夫曼树,概率越大,路径越短。
2、哈夫曼编码:在哈夫曼的每个分支上标上0或1
——结点的左分支标0,右分支标1
——把从根到每个叶子的路径上的标号连接起来,构成一个二进制串,该二进制串就成为该叶子代表的哈夫曼编码
编码
1)二维数组code存放n个叶结点的编码
2)tempCoding是一个在结尾放了空字符的临时字符串指针
3)从0~n,cur为当前数据的下标,parent为其双亲,start用于指向编码在tempCoding中的位置,初始时指向最后一个(n-1)
4)当不是根结点时,左0右1进行赋值,再把当前数据改为其双亲数据,继续向上编码
5)tempCoding+start是当前开始有编码的位置,把编码copy给code[i]
void HuffmanCoding(HuffmanTree T, char**& code, int n) {
code = new char* [n];
if (!code)
exit(OVERFLOW);
char* tempCoding = (char*)malloc(n * sizeof(char));
if (!tempCoding)
exit(OVERFLOW);
tempCoding[n - 1] = '\0';
for (int i = 0; i < n; i++) {
int cur = i;
int parent = T[i].parent;
int start = n - 1;
//当未到根结点时
while (parent != -1) {
//左0右1
if (T[parent].lChild == cur)
tempCoding[--start] = '0';
else if (T[parent].rChlid == cur)
tempCoding[--start] = '1';
cur = parent;
parent = T[cur].parent;
}
code[i] = (char*)malloc((n - start) * sizeof(char));
if (!code[i])
exit(OVERFLOW);
strcpy(code[i], tempCoding + start);
}
for (int i = 0; i < n; i++)
cout << code[i] << endl;
}
孩子兄弟树
结构体
typedef struct CSTreeNode {
CSTElemType data;
CSTreeNode* firstChild, * nextSibling;
}*CSTree;
和二叉树相比较,把左孩子右孩子换成了首孩子和兄弟
初始化和创建树
创建树时:
输入非零值创建树的根结点
和二叉树一开始的赋值类似,data存储输入的值e,让两个指针指空
结点入队
当队伍不为空时:
将输入的第一个结点存为当前结点的首孩子,将剩下的输入存为它孩子的兄弟
//初始化树
void InitTree(CSTree& T) {
T = NULL;
}
//创建树
void CreatTree(CSTree& T) {
LinkQueue Q;
CSTElemType e;
cout << "请输入根节点" << endl;
cin >> e;
CSTree p;
//有树
if (e != 0) {
InitLQ(Q);
T = (CSTree)malloc(sizeof(CSTreeNode));
if (!T)
exit(OVERFLOW);
T->data = e;
T->firstChild = NULL;
T->nextSibling = NULL;
EnLQ(Q, (LQElemType)T);
while (!LQIsEmpty(Q)) {
DeLQ(Q, (LQElemType&)p);
cout << "请输入" << p->data << "的孩子个数" << endl;
int childNum;
cin >> childNum;
//有树
if (childNum > 0) {
cout << "请输入" << p->data << "的" << childNum << "个孩子值" << endl;
cin >> e;
CSTree q;
q = (CSTree)malloc(sizeof(CSTreeNode));
if (!q)
exit(OVERFLOW);
q->data = e;
q->firstChild = NULL;
q->nextSibling = NULL;
p->firstChild = q;
p = q;
EnLQ(Q, (LQElemType)q);
for (int i = 1; i < childNum; i++) {
cin >> e;
CSTree q2 = (CSTree)malloc(sizeof(CSTreeNode));
if (!q2)
exit(OVERFLOW);
q2->data = e;
q2->firstChild = NULL;
q2->nextSibling = NULL;
p->nextSibling = q2;
p = q2;
EnLQ(Q, (LQElemType)q2);
}
}
}
DestroyLQ(Q);
}
}
遍历
前序遍历:和二叉树的前序遍历思路类似
层序遍历:输出根结点的值,入队->当队不空:输出当前结点的首孩子,按次输出该孩子的右兄弟
//前序遍历
void PreOrderTraverse(CSTree T) {
if (T) {
cout << T->data << " ";
PreOrderTraverse(T->firstChild);
PreOrderTraverse(T->nextSibling);
}
}
//层序遍历
void LevelOrderTraverse(CSTree T) {
LinkQueue Q;
CSTree p, q;
if (T) {
cout << T->data << " ";
InitLQ(Q);
EnLQ(Q, (LQElemType)T);
while (!LQIsEmpty(Q)) {
DeLQ(Q, (LQElemType&)p);
if (p->firstChild) {
q = p->firstChild;
while (q) {
EnLQ(Q, (LQElemType)q);
cout << q->data << " ";
q = q->nextSibling;
}
}
}
}
}
销毁
先深入到最后一个首孩子,再找到它的最后一个右兄弟,从右到左,从下到上(这个方向仅用于自己理解。。),向上回溯,用free()释放其空间
//销毁树
void DestroyTree(CSTree& T) {
if (T) {
DestroyTree(T->firstChild);
DestroyTree(T->nextSibling);
free(T);
T = NULL;
}
}
求树深度
//求树深度
int TreeDepth(CSTree T) {
CSTree p;
int depth, max = 0;
if (!T)
return 0;
for (p = T->firstChild; p; p = p->nextSibling) {
depth = TreeDepth(p);
if (depth > max)
max = depth;
}
return max + 1;
}
SomeFunctions
//返回p结点值
CSTElemType Value(CSTree p) {
return p->data;
}
//返回根节点值
CSTElemType Root(CSTree T) {
if (T)
return Value(T);
else
return 0;
}
//返回值为e的结点
CSTree Point(CSTree T, CSTElemType e) {
LinkQueue Q;
CSTree p;
if (T) {
InitLQ(Q);
EnLQ(Q, (LQElemType)T);
while (!LQIsEmpty(Q)){
DeLQ(Q,(LQElemType&)p);
if (p->data == e)
return p;
if (p->firstChild)
EnLQ(Q, (LQElemType)p->firstChild);
if (p->nextSibling)
EnLQ(Q, (LQElemType)p->nextSibling);
}
}
return NULL;
}
//求值为e的双亲结点值
CSTElemType Parent(CSTree T, CSTElemType e) {
LinkQueue Q;
CSTree p, q;
if (T) {
InitLQ(Q);
EnLQ(Q, (LQElemType)T);
while (!LQIsEmpty(Q)) {
DeLQ(Q, (LQElemType&)p);
if (p->firstChild) {
q = p->firstChild;
while (q && q->data != e) {
EnLQ(Q, (LQElemType)q);
q = q->nextSibling;
}
if (q)
return p->data;
}
}
}
return NULL;
}
//求值为cur_e的最左孩子
CSTElemType LeftChild(CSTree T, CSTElemType cur_e) {
CSTree p = Point(T, cur_e);
if (p && p->firstChild)
return p->firstChild->data;
return 0;
}
//求值为cur_e的右兄弟
CSTElemType rightSibling(CSTree T, CSTElemType cur_e) {
CSTree p = Point(T, cur_e);
if (p && p->nextSibling)
return p->nextSibling->data;
else
return 0;
}
//插入孩子:将c插入到p结点的i个位置
status InsertChild(CSTree& T, CSTree p, int i, CSTree c) {
int j;
CSTree q;
if (T) {
//插入到长子位置
if (i == 1) {
c->nextSibling = p->firstChild;
p->firstChild = c;
}
else {
int j = 1;
q = p->firstChild;
while (q && j < i - 1) {
q = q->nextSibling;
j++;
}
if (!q || j > i - 1) //q是插入的点前一个
return ERROR; //位置错误
c->nextSibling = q->nextSibling;
q->nextSibling = c;
}
return OK;
}
else {
return ERROR;
}
}
//删除p结点的第i棵子树
status DeleteChild(CSTree& T, CSTree p, int i) {
CSTree q, t;
if (p) {
if (i == 1) {
q = p->firstChild;
p->firstChild = q->nextSibling;
q->nextSibling = NULL;
DestroyTree(q);
}
else {
int j = 1;
q = p->firstChild;
while (q && j < i - 1) {
q = q->nextSibling;
j++;
}
if (!q || j > i - 1)
return ERROR;
t = q->nextSibling; //t是要删除的点
q->nextSibling = t->nextSibling;
t->nextSibling = NULL;
DestroyTree(t);
}
return OK;
}
else
return ERROR;
}