数据结构二叉树(下)
Posted 姜君竹
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构二叉树(下)相关的知识,希望对你有一定的参考价值。
文章目录
二叉树的链式结构
链式二叉树的增删查改是没有意义的,建立在二叉树基础上的搜索二叉树才具备增删查改的意义。但是链式二叉树是基础,就像修房子得先打地基一样。
二叉树的遍历
首先确定一个概念,二叉树分为空树和非空树,只要是非空树就有根节点、根节点的左子树和根节点的右子树。二叉树的定义是递归式的。
二叉树遍历是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作依次。访问节点所做的操作依赖于具体的应用问题,遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
二叉树一共有四种遍历方式,前序遍历(先根遍历)、中序遍历(中根遍历)、后序遍历(后根遍历)和层序遍历。
前序遍历(先根遍历)
访问根节点的操作发生在遍历其左右子树之前。在遍历时,会不断向下遍历知道遇到空树才停止。
void PrevOrder(BTNode* root)
if (NULL == root)
printf("NULL ");
return;
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
栈帧空间的创建销毁顺序按照下图进行的,向右的箭头表示创建,向左的箭头表示销毁。创建销毁的顺序是橙色->绿色->深蓝色->粉色->金色->紫色->青色。
中序遍历(中根遍历)
访问根节点的操作放生在遍历其左右子树之间。
void InOrder(BTNode* root)
if (NULL == root)
printf("NULL ");
return;
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
栈帧空间的创建销毁顺序按照下图进行的,向右的箭头表示创建,向左的箭头表示销毁。创建销毁的顺序是橙色->绿色->蓝色->粉色->深蓝色->金色->青色->紫色。
后序遍历(后根遍历)
访问根节点的操作发生在遍历其左右子树之后。
void PostOrder(BTNode* root)
if (NULL == root)
printf("NULL ");
return;
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
栈帧空间的创建销毁顺序按照下图进行的,向右的箭头表示创建,向左的箭头表示销毁。创建销毁的顺序是橙色->绿色->蓝色->粉色->深蓝色->金色->青色->紫色。
层序遍历
从第一层开始,每层从左到右,一层一层的走。四种遍历中,只有层序遍历是非递归的,它采用的是队列来实现的。
void LevelOrder(BTNode* root)
Queue q;
QueueInit(&q);
// 先将根节点存入队列
if (root)
QueuePush(&q, root);
// 判空,如果为空就不执行以下语句
while (!QueueEmpty(&q))
// 获取队头,打印之后,移出队列
BTNode* front = QueueFront(&q);
printf("%d ", front->data);
QueuePop(&q);
if (front->left)
QueuePush(&q, front->left);
if (front->right)
QueuePush(&q, front->right);
printf("\\n");
QueueDestroy(&q);
二叉树的构建
二叉树的构建根据三种递归的遍历方式进行构建,可以进行前序、中序、后序构建,我这里使用的是前序构建。
BTNode* CreatBinaryTree(BTDataType* a, int* pi)
if (-1 == a[*pi])
(*pi)++;
return NULL;
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
if (NULL == root)
perror("malloc fail");
exit(-1);
root->data = a[(*pi)++];
root->left = CreatBinaryTree(a, pi);
root->right = CreatBinaryTree(a, pi);
return root;
二叉树的节点数
int TreeSize(BTNode* root)
if (NULL == root)
return 0;
return TreeSize(root->left) + TreeSize(root->right) + 1;
栈帧空间的创建销毁顺序按照下图进行的,向右的箭头表示创建,向左的箭头表示销毁。创建销毁的顺序是橙色->绿色->蓝色->粉色->深蓝色->金色->青色->紫色,最后返回的值是6。
二叉树的叶子数
int TreeLeafSize(BTNode* root)
if (NULL == root)
return 0;
if (NULL == root->left && NULL == root->right)
return 1;
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
栈帧空间的创建销毁顺序按照下图进行的,向右的箭头表示创建,向左的箭头表示销毁。创建销毁的顺序是橙色->绿色->蓝色->粉色->深蓝色->金色,最后结果为3。
二叉树的高度
int TreeHeight(BTNode* root)
if (NULL == root)
return 0;
int left = TreeHeight(root->left);
int right = TreeHeight(root->right);
return (left > right ? left : right) + 1;
栈帧空间的创建销毁顺序按照下图进行的,向右的箭头表示创建,向左的箭头表示销毁。创建销毁的顺序是橙色->绿色->蓝色->粉色->深蓝色->金色,最后结果为3。
二叉树K层节点数
int TreeKLevelSize(BTNode* root, int k)
if (NULL == root)
return 0;
if (1 == k)
return 1;
return TreeKLevelSize(root->left, k - 1) + TreeKLevelSize(root->right, k - 1);
栈帧空间的创建销毁顺序按照下图进行的,向右的箭头表示创建,向左的箭头表示销毁。创建销毁的顺序是橙色->绿色->蓝色->粉色->深蓝色->金色,最后结果为3。
查找值为X的节点
BTNode* TreeFind(BTNode* root, BTDataType x)
if (NULL == root)
return NULL;
if (x == root->data)
return root;
BTNode* ret1 = TreeFind(root->left, x);
if (ret1)
return ret1;
BTNode* ret2 = TreeFind(root->right, x);
if (ret2)
return ret2;
return NULL;
栈帧空间的创建销毁顺序按照下图进行的,向右的箭头表示创建,向左的箭头表示销毁。创建销毁的顺序是橙色->绿色->蓝色->粉色->深蓝色->金色,最后结果为数字5节点的地址。
判断二叉树是否为完全二叉树
bool TreeComplete(BTNode* root)
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
// 若队列为空,则不执行
while (!QueueEmpty(&q))
BTNode* front = QueueFront(&q);
QueuePop(&q);
// 判断front是否为空,为空则跳出循环
if (!front)
break;
else
QueuePush(&q, front->left);
QueuePush(&q, front->right);
// 若队列为空,则不执行
while (!QueueEmpty(&q))
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front)
QueueDestroy(&q);
return false;
QueueDestroy(&q);
return true;
二叉树的销毁
// 二叉树销毁
void TreeDestory(BTNode* root)
if (NULL == root)
return;
TreeDestory(root->left);
TreeDestory(root->right);
free(root);
数据结构二叉树
线索二叉树的定义
对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域。
利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树。
线索二叉树分为先序、中序和后序线索二叉树。
对二叉树以某种方式遍历使其变为线索二叉树的过程称为线索化。
在原二叉链中增加了ltag和rtag两个标志域。
以中序线索二叉树为例
线索化二叉树
中序线索化二叉树类ThreadTree
采用中序遍历进行中序线索化
在整个算法中p总是指向当前访问的结点,pre指向其前驱结点。
遍历线索化二叉树
该算法是一个非递归算法,算法的时间复杂度为O(n),空间复杂度为O(1)
哈夫曼树的定义
给定4个叶子结点,设其权值分别为1、3、5、7,可以构造出形状不同的4棵二叉树。
哈夫曼树的构造算法
(1)根据给定的n0个权值W=(w1,w2,…,wn0),对应结点构成n0棵二叉树的森林T=(T1,T2,…,Tn0),其中每棵二叉树Ti(1≤i≤n0)中都只有一个带权值为wi的根结点,其左、右子树均为空。
(2)在森林T中选取两棵根结点权值最小的子树作为左、右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左、右子树上根的权值之和。称为合并,每合并一次T中减少一棵二叉树。
(3)重复(2)直到T只含一棵树为止。这棵树便是哈夫曼树。
每次都是两棵子树合并 ð 哈夫曼树中没有单分支结点
W=(1,3,5,7)来构造一棵哈夫曼树
定理6.3 对于具有n0个叶子结点的哈夫曼树,共有2n0-1个结点。
证明:从哈夫曼树的构造过程看出,每次合并都是将两棵二叉树合并为一个,所以哈夫曼树不存在度为1的结点,即n1=0。
由二叉树的性质1可知n0=n2+1,即n2=n0-1
则结点总数n=n0+n1+n2= n0+n2=n0+2n0-1=2n0-1。
构造哈夫曼树中采用静态数组ht存储哈夫曼树,即每个数组元素存放一个结点。设计哈夫曼树中结点类如下:
哈夫曼编码
构造一棵哈夫曼树。
规定哈夫曼树中的左分支为0,右分支为1。
从根结点到每个叶子结点所经过的分支对应的0和1组成的序列便为该结点对应字符的编码。这样的编码称为哈夫曼编码。
哈夫曼编码的实质就是使用频率越高的采用越短的编码。
只有ht[0..n0-1]叶子结点才对应哈夫曼编码,用hcd[i](0≤i≤n0-1)表示ht[i]叶子结点的哈夫曼编码。
输出所有叶子结点的哈夫曼编码的算法
输出ht的算法
在一组字符的哈夫曼编码中,任一字符的哈夫曼编码不可能是另一字符哈夫曼编码的前缀。
树到二叉树的转换及还原
树到二叉树的转换
一棵树可以按照以下规则转换为二叉树:
加线:在各兄弟结点之间加一连线,将其隐含的“兄-弟”关系以“双亲-右孩子”关系显示表示出来。
抹线:对任意结点,除了其最左子树之外,抹掉该结点与其他子树之间的“双亲-孩子”关系。
调整:以树的根结点作为二叉树的根结点,将树根与其最左子树之间的“双亲-孩子”关系改为“双亲-左孩子”关系,且将各结点按层次排列,形成二叉树。
根结点只有左子树而没有右子树。
左分支不变(左分支为最左孩子),兄弟变成右分支(右分支实为双亲的兄弟)。
树中分支结点个数为m,则二叉树中无右孩子的结点个数为m+1。这里m=4。
一棵由树转换的二叉树还原为树
一棵二叉树(由一棵树转换的)可以按照以下规则还原为一棵树:
加线:在各结点的双亲与该结点右链上的每个结点之间加一连线,以“双亲-孩子”关系显示表示出来。
抹线:抹掉二叉树中所有双亲结点与其右孩子之间的“双亲-右孩子”关系。
调整:以二叉树的根结点作为树的根结点,将各结点按层次排列,形成树。
根结点不变。
左分支不变(左分支为最左孩子),右分支变成兄弟。
森林到二叉树的转换及还原
森林转换为二叉树
含有两棵或两棵以上树的森林可以按照以下规则转换为二叉树:
转换:将森林中的每一棵树转换成二叉树,设转换成的二叉树为bt1、bt2、…、btm。
连接:将各棵转换后的二叉树的根结点相连。
调整:以bt1的根结点作为整个二叉树的根结点,将bt2的根结点作为bt1的根结点的右孩子,将bt3的根结点作为bt2的根结点的右孩子,…,如此这样得到一棵二叉树,即为该森林转换得到的二叉树。
二叉树还原为森林
当一棵二叉树的根结点有m-1个右下孩子,这样还原的森林中有m棵树。这样的二叉树可以按照以下规则还原其相应的森林:
抹线:抹掉二叉树根结点右链上所有结点之间的“双亲-右孩子”关系,分成若干个以右链上的结点为根结点的二叉树,设这些二叉树为bt1、bt2、…、btm。
转换:分别将bt1、bt2、…、btm二叉树各自还原成一棵树。
调整:将转换好的树构成森林。
以上是关于数据结构二叉树(下)的主要内容,如果未能解决你的问题,请参考以下文章