1 作业题目
6.1 按先序遍历的扩展序列建立一棵二叉树的二叉链表存储结构。注意,关于“先序遍历的扩展序列”请参考课程幻灯片的第92屏至第94屏。
6.2 分别实现二叉树先序、中序、后序遍历的递归算法。
6.3 (参见教材:《数据结构(C语言版)》,第6章,第130页与第131页)实现二叉树中序遍历的非递归算法。
6.4 (《数据结构题集(C语言版)》,第6章,第41页,第6.27题,难度系数为3)假设一颗二叉树的先序遍历序列为EBADCFHGIKJ和中序遍历序列为ABCDEFGHIJK,
(1)请画出该树。说明你所采用的画图工具软件名称,要求在你所采用的画图工具软件之下,多画出的图示是可修改的。
(2)设计与实现你的算法,并设计一种简明的输出一棵二叉树的方式。
实现代码:
6.5 (《数据结构题集(C语言版)》,第6章,第41页,第6.28题,难度系数为3)假设一颗二叉树的中序序列为DCBGEAHFIJK和后序序列为DCEGBFHKJIA,
(1)请画出该树。说明你所采用的画图工具软件名称,要求在你所采用的画图工具软件之下,多画出的图示是可修改的。
(2)设计与实现你的算法,并设计一种简明的输出一棵二叉树的方式。
6.6 (《数据结构题集(C语言版)》,第6章,第44页,第6.65题,难度系数为4)已知一颗二叉树的前序序列和中序序列分别存于两个一维数组中,试编写算法建立该二叉树的二叉链表。
要求:提供两个结点数目不少于10个的不同的典型二叉树,来测试你所设计的程序。
6.7 (《数据结构题集(C语言版)》,第6章,第42页,第6.29题,难度系数为3)假设一颗二叉树的层序遍历序列为ABCDEFGHIJ和中序遍历序列为DBGEHJACIF,
(1)请画出该树。说明你所采用的画图工具软件名称,要求在你所采用的画图工具软件之下,多画出的图示是可修改的。
(2)设计与实现你的算法,并设计一种简明的输出一棵二叉树的方式。
6.8 (《数据结构题集(C语言版)》,第6章,第43页,第6.47题,难度系数为4)实现二叉树层次遍历的非递归算法,并总结应用队列解决问题的基本模式。
6.9 应用后序遍历方式,求二叉树的深度。
请参考课程幻灯片的第90屏与第91屏。
6.10 (数据结构题集(C语言版)》,第6章,第44页,第6.66题,难度系数为4)假设有n个结点的树T采用了双亲表示法,写出由此建立一般树的二叉树表示法的存储结构,即孩子-兄弟(链表)表示法,或二叉链表表示法(参见教材:《数据结构(C语言版)》,第6章,第136页)。
要求:提供两个结点数目不少于10个的不同的树,来测试你所设计的程序。
6.11 (《数据结构题集(C语言版)》,第6章,第44页,第6.63题,难度系数为3)求树的深度。
要求:提供两个结点数目不少于10个的不同的树,来测试你所设计的程序。
6.12 (《数据结构题集(C语言版)》,第6章,第44页,第6.68题,难度系数为3)已知一颗树的由根至叶子结点按层次输入的结点序列及每个结点的度(每层中自左至右输入),试写出构造此树的孩子-兄弟链表的算法。
6.13 采用自然语言或伪代码形式,分析与总结以下算法:
(1) 二叉树先序遍历方式的线索化算法;
(2) 二叉树中序遍历方式的线索化算法;
(3) 二叉树后序遍历方式的线索化算法;
(4) 对于先序线索化二叉树,如何求任意一个结点的前驱结点;
(5) 对于先序线索化二叉树,如何求任意一个结点的后继结点;
(6) 对于中序线索化二叉树,如何求任意一个结点的前驱结点;
(7) 对于中序线索化二叉树,如何求任意一个结点的后继结点;
(8) 对于后序线索化二叉树,如何求任意一个结点的前驱结点;
(9) 对于后序线索化二叉树,如何求任意一个结点的后继结点。
6.14 (《数据结构题集(C语言版)》,第6章,第41页,第6.26题,难度系数为3)假设用于通信的电文仅由8个字母组成,字母在电文中出现的频率分别为0.07, 0.19, 0.02, 0.06, 0.32, 0.03, 0.21, 0.10。试为这8个字母设计哈夫曼编码。使用0~7的二进制表示形式是另一种编码方案,对于上述实例,比较两种方案的优缺点。
特别说明:本章作业还是比较多的,而且有一定难度。
2 作业题目解答
【6.1题解答】:
根据本课程幻灯片的第92屏至第94屏,
算法思路:使用先序的方式和创建二叉树,对每个结点进行依次输入,如果输入为‘ ’,表明为空结点, 否则分配空间并依次对它的左右子树进行操作。
代码实现(这里只展示建立存储结构的代码,完整代码见6.2)
#include <stdio.h>
#include <stdlib.h>
typedef struct BiTNode
{
char data;//二叉树节点数据
struct BiTNode* lchild;//二叉树节点左子树指针
struct BiTNode* rchild;//二叉树节点右子树指针
}BiTNode, * BiTree;//二叉树节点
void CreateBiTree(BiTree* T)
{
char ch;
scanf("%c", &ch);
if (' ' == ch)
{
*T = NULL;
}
else
{
*T = (BiTree)malloc(sizeof(BiTNode));
(*T)->data = ch;
CreateBiTree(&((*T)->lchild));
CreateBiTree(&((*T)->rchild));
}
}
【6.2题解答】:
(1)先序遍历:对每一个结点访问,如果它不为空,则访问该结点的值,再依次访问它的左子树和右子树。对下一个结点执行同样的操作。
(2)中序遍历:对每一个结点访问,如果它不为空,访问该结点的左子树,再访问它的值,最后访问右子树。对下一个结点知行同样的操作。
(3)后序遍历:对每一个结点访问,如果它不为空,访问该结点的左子树,再访问该结点的右子树,最后访问它的值,对下一个结点执行同样的操作。
代码实现:
#include <stdio.h>
#include <stdlib.h>
typedef struct BiTNode
{
char data;//二叉树节点数据
struct BiTNode* lchild;//二叉树节点左子树指针
struct BiTNode* rchild;//二叉树节点右子树指针
}BiTNode, * BiTree;//二叉树节点
//二叉树先序遍历
void PreBiTree(BiTree T)
{
if (NULL == T)
{
return;
}
printf("%c ", T->data);
PreBiTree(T->lchild);
PreBiTree(T->rchild);
}
//二叉树中序遍历
void InBiTree(BiTree T)
{
if (NULL == T)
{
return;
}
InBiTree(T->lchild);
printf("%c ", T->data);
InBiTree(T->rchild);
}
//二叉树后序遍历
void PostBiTree(BiTree T)
{
if (NULL == T)
{
return;
}
PostBiTree(T->lchild);
PostBiTree(T->rchild);
printf("%c ", T->data);
}
//创建二叉树
void CreateBiTree(BiTree* T)
{
char ch;
scanf("%c", &ch);
if (' ' == ch)
{
*T = NULL;
}
else
{
*T = (BiTree)malloc(sizeof(BiTNode));
(*T)->data = ch;
CreateBiTree(&((*T)->lchild));
CreateBiTree(&((*T)->rchild));
}
}
int main()
{
BiTree T = NULL;
printf("请按照先序遍历的顺序输入要创建的二叉树\n");
CreateBiTree(&T);
printf("二叉树的先序遍历:");
PreBiTree(T);
printf("\n");
printf("二叉树的中序遍历:");
InBiTree(T);
printf("\n");
printf("二叉树的后序遍历:");
PostBiTree(T);
printf("\n");
return 0;
}
所用测试二叉树如图所示:
运行结果截图:
【6.3题解答】:
(参见教材:《数据结构(C语言版)》,第6章,第130页与第131页)实现二叉树中序遍历的非递归算法。
实现代码:
Chapter 6_3.cpp
#include<stdio.h>
#include <stdlib.h>
#define STACKINITSIZE 20//栈初始空间大小
#define INCREASEMENT 10//栈空间大小的增量
typedef struct BiTNode
{
char data;//二叉树节点数据
BiTNode* lchild, * rchild;//指向二叉树左子树和右子树的指针
}BiTNode, * BiTree;//定义二叉树节点结构
typedef struct SqStack
{
BiTNode* base;//栈底指针
BiTNode* top;//栈顶指针
int stacksize;//顺序栈空间大小
}SqStack;//定义顺序栈结构
//建立一个空栈,建立成功,返回1,失败,返回0
int InitStack(SqStack& S)
{
S.base = (BiTNode*)malloc(STACKINITSIZE * sizeof(BiTNode));
if (!S.base)
return 0;
S.top = S.base;
S.stacksize = STACKINITSIZE;
return 1;
}
//进栈操作
int Push(SqStack& S, BiTNode e)
{
if (S.top - S.base >= S.stacksize)
{
S.base = (BiTNode*)realloc(S.base, (STACKINITSIZE + INCREASEMENT) * sizeof(BiTNode));
if (!S.base)
return 0;
S.stacksize = 30;
}
*S.top = e;
S.top++;
return 1;
}
//出栈操作,若栈为空,则返回0;栈不为空,则返回1
int Pop(SqStack& S, BiTNode& e)
{
if (S.base == S.top)
return 0;
S.top--;
e = *S.top;
return 1;
}
//判断栈是否为空,若栈为空,则返回true,栈不为空,则返回false
bool StackEmpty(SqStack S)
{
if (S.base == S.top)
return true;
else
return false;
}
//建立二叉树
void CreateBiTree(BiTree& T)
{
char ch;
scanf("%c", &ch);
if (ch == ' ')
T = NULL;
else
{
T = (BiTNode*)malloc(sizeof(BiTNode));
T->data = ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
//中序遍历二叉树
int InOrderTraverse(BiTree T)
{
if (!T)
return 0;
SqStack S;
int n = InitStack(S);//建立空栈
if (!n)
return 0;
BiTree p = T;
BiTNode e;//二叉树节点,用于存放从栈中取出的节点
while (p || !StackEmpty(S))
{
if (p)
{
Push(S, *p);
p = p->lchild;
}
else
{
Pop(S, e);
printf("%c ", e.data);
p = e.rchild;
}
}
printf("\n");
return 1;
}
int main()
{
BiTree T = NULL;
printf("请输入二叉树-按照先序序列建立二叉树\n");
CreateBiTree(T);
printf("中序遍历二叉树结果为:\n");
InOrderTraverse(T);
return 0;
}
运行结果截图:
【6.4题解答】:
《数据结构题集(C语言版)》,第6章,第41页,第6.27题,难度系数为3。
(1)画出该树:采用的画图软件:visio,只有一种情况,如图所示
(2)算法与输出:
实现代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char TElemType;
typedef struct BiNode {
TElemType data;
struct BiNode* lchild;
struct BiNode* rchild;
}BiNode, * BiTree;
typedef BiTree ElemType;
typedef int Status;
#define OK 1
#define ERROR 0
#define MAXSIZE 100
typedef struct {
ElemType* space;
int front, rear;
}Queue;
void printTree(BiNode* n, int type, int level) {
int i;
if (NULL == n)
return;
//先找到右节点将其输出,在全部查找与输出完成后,
//回到最近的左节点输出并继续重复右节点的查找与输出,并运用递归不断向根节点移动
printTree(n->rchild, 2, level + 1);
switch (type) {
case 0: {
//当前结点输出时,表示根节点右侧全部输出完成,
//此时从左节点开始进行右节点查找并进行新的递归操作
printf("%2c\n", n->data);
break; }
case 1: {
for (int i = 0; i < level; i++)
printf("\t");
printf("\\\n");
for (int i = 0; i < level; i++)
printf("\t");
printf("%2c\n", n->data);
break;//右节点输出
}
case 2: {
for (int i = 0; i < level; i++)
printf("\t");
printf(" %2c\n", n->data);
for (int i = 0; i < level; i++)
printf("\t");
printf("\n");
break;//左节点输出
}
}
printTree(n->lchild, 1, level + 1);//无右节点时寻找左节点并进行递归查找和输出
}
//二叉树输出,type 0表示根节点,1表示做左节点,2表示右节点
//level表示层次,用于控制显示的距离
BiTree CreatePM(TElemType * prestr, TElemType * midstr, int length) {
if (length == 0) return NULL;
TElemType* p;
int len_lchild = 0, len_rchild = 0;
BiNode* root = (BiNode*)malloc(sizeof(BiNode));
root->data = *prestr;
for (p = midstr; p < midstr + (length); p++) {
if (*p == *prestr)
break;
len_lchild++;
}
root->lchild = CreatePM(prestr + 1, midstr, len_rchild);
len_rchild = (length)-1 - len_lchild;
root->rchild = CreatePM(prestr + 1 + len_lchild, p + 1, len_rchild);
return root;
}//通过前序中序建立二叉树
//先序遍历的第一个字符为根节点,对于中序遍历,根节点在中序遍历的中间。
//左边部分是左子树,右边是右子树,运用递归构造二叉树
void Destroy(BiTree* T) {
if (*T == NULL)
return;
Destroy(&((*T)->lchild));
Destroy(&((*T)->rchild));
free(*T);
*T = NULL;
}
void PreOrder(BiTree T) {
if (T = NULL)
return;
printf("%c", T->data);
PreOrder(T->lchild);
PreOrder(T->rchild);
}//前序遍历
void MidOrder(BiTree T) {
if (T == NULL)
return;
MidOrder(T->lchild);
printf("%c", T->data);
MidOrder(T->rchild);
}//中序遍历
void PostOrder(BiTree T) {
if (T == NULL)
return;
PostOrder(T->lchild);
PostOrder(T->rchild);
printf("%c", T->data);
}//后序遍历
Status InitQueue(Queue * Qp) {
Qp->space = (ElemType*)malloc(sizeof(ElemType) * MAXSIZE);
if (!Qp->space)
return ERROR;
Qp->front = 0;
Qp->rear = 0;
return OK;
}
Status DestroyQueue(Queue * Qp) {
if (!Qp->space)
return ERROR;
free(Qp->space);
Qp->space = NULL;
return OK;
}
Status ClearQueue(Queue * Qp) {
if (!Qp->space)
return ERROR;
Qp->front = 0;
Qp->rear = 0;
return OK;
}
Status EnQueue(Queue * Qp, ElemType e) {
int next = (Qp->rear + 1) % MAXSIZE;
if (!Qp->space || next == Qp->front)
return ERROR;
Qp->space[next] = e;
Qp->rear = next;
return OK;
}
Status DeQueue(Queue * Qp, ElemType * ep) {
int first = (Qp->front + 1) % MAXSIZE;
if (!Qp->space || Qp->front==Qp->rear)
return ERROR;
*ep = Qp->space[first];
Qp->front = first;
return OK;
}
Status IsEmptyQueue(Queue Q) {
return Q.front = Q.rear;
}
Status PrintQueue(Queue Q, char split){
int p = Q.front;
while (p != Q.rear) {
p = (p + 1) % MAXSIZE;
printf("%d",Q.space[p]);
printf("%c",split);
}
printf("\n");
return OK;
}
//void LevelOrder(BiTree T) {
// if (T == NULL)
// return;
// Queue Q;
// BiTree p;
// InitQueue(&Q);
// EnQueue(&Q, T);
// while (!IsEmptyQueue(Q)) {
// DeQueue(&Q, &p);
// printf("%c", p->data);
// if (p->lchild)
// EnQueue(&Q, p->lchild);
// if (p->rchild)
// EnQueue(&Q, p->rchild);}
// }//层序遍历
int main() {
BiTree T;
int order, location, length;
char data[100], prestr[100], midstr[100];
printf("请输入前序遍历");
scanf("%s", prestr);
printf("请输入中序遍历:");
scanf("%s", midstr);
length = strlen(prestr);
T = CreatePM(prestr, midstr, length+1);
printf("后序遍历:");
PostOrder(T); printf("\n");
//printf("层次遍历:");
// LevelOrder(T); printf("\n");
printf("二叉树型:");
printTree(T, 0, 0);
Destroy(&T); printf("\n");
}
运行结果截图:
【6.5题解答】:
《数据结构题集(C语言版)》,第6章,第41页,第6.28题,难度系数为3。
(1)画出该树:
(2)算法与输出
算法思路:找到中序中的第一个结点在后序中的位置,然后以其两端分界来再次递归。
算法思路:找到中序中的第一个结点在后序中的位置,然后以其两端分界来再次递归。
BiTree Construct(char *startIn,char *endIn,char *startPost,char *endPost){
//后序遍历的最后一个必然是根结点
BiTree root=(BiTree)malloc(sizeof(BiTree));
root->data=*endPost;
root->lchild=NULL;
root->rchild=NULL;
//递归终止条件:后序遍历中只有一个结点
if(startPost==endPost) return root;
//在中序遍历中找到根结点
char *rootIn=startIn;
while(rootIn<=endIn&&(*rootIn)!=root->data)
rootIn++;
int leftlen=rootIn-startIn;//左子树的结点数
if(leftlen>0)//左子树不为空,构造左子树
root->Lchild=Construct(startIn,rootIn-1,startPost,startPost+leftlen-1);
int rightlen=endIn-rootIn;//右子树的结点数
if(rightlen>0)//右子树不为空,构造右子树
root->Rchild=Construct(rootIn+1,endIn,startPost+leftlen,endPost-1);
return root;
}
打印方法:
思路:先遍历经过树中的每个结点,记录下该结点在树中的行列值。最后以一个二维列表的形式打印树。
Status PrintTree(BiTree T) {
typedef struct {
ElemType e;
int x;
int y;
}Node;
SqStack S;
SElemType tmp;
Node node[100];
int row, col, row_max, k, i, j;
char a[100][100] = {};
int m, n;
k = 0;
if (T) {
InitStack(&S);
Push(&S, T);
row = col = 0;
row_max = 0;
while (!StackEmpty(S)) {
GetTop(S, &tmp);
node[k].e = tmp->data;
node[k].x = col;
node[k].y = row;
k++;
while (tmp->lchild) {
Push(&S, tmp->lchild);
row++;
if (row_max < row)
row_max = row;
GetTop(S, &tmp);
node[k].e = tmp->data;
node[k].x = col;
node[k].y = row;
k++;
}
Pop(&S, &tmp);
if (tmp->rchild) {
Push(&S, tmp->rchild);
col++;
}
else {
while (!StackEmpty(S)) {
Pop(&S, &tmp);
row--;
if (tmp->rchild) {
Push(&S, tmp->rchild);
col++;
row++;
break;
}
}
}
}
for (int i = 0; i < k; i++) {
a[node[i].x][3 * node[i].y] = node[i].e;
}
for (int i = 0; i <= col; i++) {
for (j = 0; j <= 3 * row_max; j++) {
if (a[i][j])
printf("%c", a[i][j]);
else
printf(".");
}
printf("\n");
}
}
return OK;
}
运行结果截图:
【6.6题解答】:
《数据结构题集(C语言版)》,第6章,第44页,第6.65题,难度系数为4。
算法思想:根据二叉树的先序遍历序列和中序遍历序列可以创建一棵唯一的二叉树。先序遍历的第一个结点,是二叉树的根节点,在中序遍历找到根结点后,可以知道根节点的左右子树的结点和左右子树的结点数(用llen和rlen表示),然后递归分别建立其左右子树,依次扫描二叉树先序序列,然后在中序序列中找到该结点从而确定该结点下的左右子树,直到左右子树的结点数为0时(llen==0和rlen==0),二叉树建立完毕。
实现代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
typedef char ElemType;
typedef struct BiNode {
ElemType data;
struct BiNode* lchild, * rchild;
}BiTNode,*BiTree;
BiTree CreatBiTree(ElemType A[], ElemType B[], int la, int ha, int lb, int hb) {
//A[]存放前序序列,B[]存放中序序列,la,ha表示前序起始位置和终止位置
int llen, rlen;//中序序列中左右子树的长度
int s = 0;
BiTree T = (BiTree)malloc(sizeof(BiTNode));
T->data = A[la];
T->lchild = T->rchild = NULL;
while (A[la] != B[s])
s++;
llen = s - lb;
rlen = hb - s;
if (llen != 0)
T->lchild = CreatBiTree(A, B, la + 1, la + llen, lb, lb + llen - 1);
else
T->lchild = NULL;//左子树长度为0,左孩子为空
if (rlen != 0)
T->rchild = CreatBiTree(A, B, ha - rlen + 1, ha, hb - rlen + 1, hb);
else
T->rchild = NULL;//7右子树长度为0,右子树为空
return T;
}
void PostOrder(BiTree T) {
if (T != NULL) {
PostOrder(T->lchild);
PostOrder(T->rchild);
printf("%c ", T->data);
}
}
int main() {
int n;
printf("请输入总结点的个数\n");
scanf("%d", &n);
char* A = (char*)malloc(sizeof(char) * n);
char* B = (char*)malloc(sizeof(char) * n);
printf("请输入前序遍历:\n");
for (int i = 0; i < n; i++)
scanf("%c ", &A[i]);
printf("请输入中序遍历:\n");
for (int i = 0; i < n; i++)
scanf("%c ", &B[i]);
BiTree T = CreatBiTree(A, B, 0, n - 1, 0, n - 1);
printf("后序遍历为:\n");
PostOrder(T);
return 0;
}
(1)测试实例1
(2)测试实例2:
【6.7题解答】:
《数据结构题集(C语言版)》,第6章,第42页,第6.29题,难度系数为3。
(1)画出该树
(2)算法及其输出:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct BNode {
char data;
struct BNode* lchild;
struct BNode* rchild;
}BNode;
#define ElemType char;
const int N = 100;
BNode* LevelInCreate(char level[], char in[], int n) {
char left[N];//存储左子树的层序遍历
char right[N];//存储右子树的层序遍历
int lcnt = 0, rcnt = 0;
if (n == 0)return NULL;
BNode* root = (BNode*)malloc(sizeof(BNode));
root->data = level[0];
int i;
for (i = 0; in[i] != root->data; i++);//中序遍历找到根节点
for (int k = 0; k < n; k++) {
//查找左子树的层序遍历
for (int l = 0; l < i; l++) {
if (in[l] == level[k])
left[lcnt++] = level[k];
}
//查找右子树的层序遍历
for (int m = i + 1; m < n; m++) {
if (in[m] == level[k])
right[rcnt++] = level[k];
}
}
root->lchild = LevelInCreate(left, in, lcnt);
root->rchild = LevelInCreate(right, in + i + 1, rcnt);
return root;
}
void PreOrder(BNode* root) {
if (root != NULL) {
printf("%c ", root->data);
PreOrder(root->lchild);
PreOrder(root->rchild);
}
}
int PrintTree(BNode T) {
typedef struct {
ElemType e;
int x;
int y;
}Node;
SqStack S;
SElemType tmp;
Node node[100];
int row, col, row_max, k, i, j;
char a[100][100] = {};
int m, n;
k = 0;
if (T) {
InitStack(&S);
Push(&S, T);
row = col = 0;
row_max = 0;
while (!StackEmpty(S)) {
GetTop(S, &tmp);
node[k].e = tmp->data;
node[k].x = col;
node[k].y = row;
k++;
while (tmp->lchild) {
Push(&S, tmp->lchild);
row++;
if (row_max < row)
row_max = row;
GetTop(S, &tmp);
node[k].e = tmp->data;
node[k].x = col;
node[k].y = row;
k++;
}
Pop(&S, &tmp);
if (tmp->rchild) {
Push(&S, tmp->rchild);
col++;
}
else {
while (!StackEmpty(S)) {
Pop(&S, &tmp);
row--;
if (tmp->rchild) {
Push(&S, tmp->rchild);
col++;
row++;
break;
}
}
}
}
for (int i = 0; i < k; i++) {
a[node[i].x][3 * node[i].y] = node[i].e;
}
for (int i = 0; i <= col; i++) {
for (j = 0; j <= 3 * row_max; j++) {
if (a[i][j])
printf("%c", a[i][j]);
else
printf(".");
}
printf("\n");
}
}
return 1;
}
int main() {
BNode* root;
char level[N];
char in[N];
printf("请输入层序遍历:\n");
scanf("%s", level);
printf("请输入中序遍历:\n");
scanf("%s", in);
int n = strlen(level);
root = LevelInCreate(level, in, n);
PreOrder(root);
return 0;
}
运行结果截图:
【6.8题解答】:
《数据结构题集(C语言版)》,第6章,第43页,第6.47题,难度系数为4。
非递归算法:
算法思路:遍历层序,在中序中寻找该结点,并递归建树。
实现代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define INITQUEUE 20
typedef struct BiTNode
{
char data;//节点数据
struct BiTNode* lchild;//节点左孩子指针
struct BiTNode* rchild;//节点右孩子指针
}BiTNode, * BiTree;//二叉树节点
typedef struct Queue
{
BiTNode* front;//队列头指针
BiTNode* tail;//队列尾指针
int size;//队列空间大小
}Queue;
//创建队列
int InitQueue(Queue& Q)
{
Q.front = (BiTNode*)malloc(INITQUEUE * sizeof(BiTNode));
if (!Q.front)
{
return 0;
}
Q.tail = Q.front;
Q.size = INITQUEUE;
return 1;
}
//判断队列是否为空
bool EmptyQueue(Queue Q)
{
if (Q.tail == Q.front)
{
return true;
}
else
{
return false;
}
}
//入队列
int EnQueue(Queue& Q, BiTNode e)
{
if ((Q.tail - Q.front + INITQUEUE) % INITQUEUE == INITQUEUE - 1)
{
return 0;
}
*Q.tail = e;
Q.tail++;
return 1;
}
//出队列
int DeQueue(Queue& Q, BiTNode& e)
{
if (Q.front == Q.tail)
{
return 0;
}
e = *Q.front;
Q.front++;
return 1;
}
//创建二叉树
void CreateBiTree(BiTree& T)
{
char ch;
scanf("%c", &ch);
if (' ' == ch)
{
T = NULL;
}
else
{
T = (BiTree)malloc(sizeof(BiTNode));
T->data = ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
//二叉树的层次遍历
int levelTraverse(BiTree T)
{
if (NULL == T)
{
return 0;
}
BiTNode e;
Queue Q;
int levelcount = 0;//树的深度
int curlevel = 1;//本层剩余的未访问的节点数
int nextlevel = 0;//下一层未访问的节点数
InitQueue(Q);
EnQueue(Q, *T);
while (!EmptyQueue(Q))
{
DeQueue(Q, e);
printf("%c ", e.data);
curlevel--;
if (NULL != e.lchild)
{
EnQueue(Q, *e.lchild);
nextlevel++;
}
if (NULL != e.rchild)
{
EnQueue(Q, *e.rchild);
nextlevel++;
}
if (0 == curlevel)
{
levelcount++;
printf("\n");
//printf("第%d层节点遍历结束!\n", levelcount);
curlevel = nextlevel;
nextlevel = 0;
}
}
return 1;
}
int main( )
{
BiTree T = NULL;
printf("请按前序输入二叉树:\n");
CreateBiTree(T);
printf("二叉树的层次遍历:\n");
levelTraverse(T);
return 0;
}
测试用例及运行结果:
总结应用队列解决问题的基本模式:
队列的根本逻辑是先进先出,对于访问顺序有这样的讲究的优先采用队列。并且队列很多时候是采用了出列即访问,并且会将它关联的内容入列。
【6.9题解答】:
根据课程幻灯片的第90屏与第91屏,应用后序遍历方式
求二叉树深度的算法如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
typedef struct BiTNode
{
char data;//节点数据
struct BiTNode* lchild;//节点左孩子指针
struct BiTNode* rchild;//节点右孩子指针
struct BiTNode* Parent;
}BiTNode, * BiTree;//二叉树节点
//递归求二叉树深度
int PostTreeDepth(BiTNode* root)
{
int leftdepth, rightdepth, max;
if (root != NULL)
{
leftdepth = PostTreeDepth(root->lchild);
rightdepth = PostTreeDepth(root->rchild);
max = leftdepth > rightdepth ? leftdepth : rightdepth;
return (max + 1);
}
else
return 0;
}
void CreateBiTree(BiTree& T)
{
char ch;
scanf("%c", &ch);
if (' ' == ch)
{
T = NULL;
}
else
{
T = (BiTree)malloc(sizeof(BiTNode));
T->data = ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
int main()
{
int depth;
BiTree T = NULL;
printf("请按前序输入二叉树:\n");
CreateBiTree(T);
depth=PostTreeDepth(T);
printf("二叉树的深度为:%d", depth);
return 0;
}
【6.10题解答】:
数据结构题集(C语言版)》,第6章,第44页,第6.66题,难度系数为4。
#define _CRT_SECURE_NO_WARNINGS
/*-------------------
|树-孩子兄弟表达法 |
-------------------*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#ifndef BASE
#define BASE
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
#endif
#define TElemType char
typedef struct CSNode {
TElemType data;
struct CSNode* firstchild, * nextsibling;
}CSNode, * CSTree;
/*-------------------
| 输出T的所有边 |
-------------------*/
void TreePrintEdge(CSTree T) {
CSNode* p;
for (p = T->firstchild; p; p = p->nextsibling) {
printf("(%c,%c)\n", T->data, p->data); //输出T的孩子
TreePrintEdge(p); //输出p的孩子
}
}
/*-------------------------
|统计叶子结点的个数 |
-------------------------*/
int TreeLeafCnt(CSTree T) {
// 树的叶子结点-->没有孩子
int ret = 0;
CSNode* p;
if (!T) return 0;
else if (!T->firstchild) return 1;
else {
for (p = T->firstchild; p; p = p->nextsibling) ret += TreeLeafCnt(p);
return ret;
}
}
/*-------------------------
|求树的度 |
-------------------------*/
int TreeDegree(CSTree T) {
// 最大的孩子数
int max = -1;
int cnt = 0;
CSNode* child;
if (!T) return -1; //空树
else if (!T->firstchild) return 0; //只有一个根结点,度为0
else {
for (cnt = 0, child = T->firstchild; child; child = child->nextsibling) cnt++; //求自己的度
max = cnt; //当前的最大值
for (child = T->firstchild; child; child = child->nextsibling) {
cnt = TreeDegree(child);
if (cnt > max) max = cnt;
}
return max;
}
}
/*-------------------------
|求树的深度 |
-------------------------*/
int TreeDepth(CSTree T) {
int h1, h2;
if (!T) return 0;
else {
h1 = TreeDepth(T->firstchild) + 1; //T孩子的深度+1
h2 = TreeDepth(T->nextsibling); //T兄弟的深度
return h1 > h2 ? h1 : h2;
}
}
/*---------------------------------
|双亲表示法-->孩子兄弟表达式|
---------------------------------*/
#define MAX_TREE_SIZE 50
typedef struct PTNode {
TElemType data;
int parent; //双亲的位置域
}PTNode;
typedef struct {
PTNode nodes[MAX_TREE_SIZE];
int r, n;
}PTree;
CSTree CreateCSTreeByPTree(PTree T) {
CSNode* tmp[MAX_TREE_SIZE]; //创建一个辅助的数组,仿照PTree结点的位置存放
CSNode* p, * q;
int i, parent;
if (T.n <= 0) return NULL;
for (i = 0; i < T.n; i++) { //双亲表按层序存储
//创建新结点
p = (CSNode*)malloc(sizeof(CSNode)); if (!p) exit(OVERFLOW);
//赋值
p->data = T.nodes[i].data; p->firstchild = p->nextsibling = NULL;
//连接
parent = T.nodes[i].parent; //父亲
if (parent != -1) { //不是根结点
if (tmp[parent]->firstchild == NULL) tmp[parent]->firstchild = p; //第一个孩子
else { //不是第一个孩子
for (q = tmp[parent]->firstchild; q->nextsibling; q = q->nextsibling); //找到最后一个孩子
q->nextsibling = p; //连接
}
}
tmp[i] = p;
}
return tmp[0];
}
int main() {
PTree PT;
CSTree CST;
PT.n = 10; PT.r = 0;
/*char ch; int tag,t;
printf("请输入结点个数:\n");
scanf("%d", &t);
PT.n = t;
PT.r = 0;
printf("请输入双亲及其位置:\n");
for (int i = 0; i < PT.n; i++) {
scanf("%c %d", &ch, &tag);
PT.nodes[i].data = ch;
PT.nodes[i].parent = tag;}*/
PT.nodes[0].data = 'R'; PT.nodes[0].parent = -1;
PT.nodes[1].data = 'A'; PT.nodes[1].parent = 0;
PT.nodes[2].data = 'B'; PT.nodes[2].parent = 0;
PT.nodes[3].data = 'C'; PT.nodes[3].parent = 0;
PT.nodes[4].data = 'D'; PT.nodes[4].parent = 1;
PT.nodes[5].data = 'E'; PT.nodes[5].parent = 1;
PT.nodes[6].data = 'F'; PT.nodes[6].parent = 3;
PT.nodes[7].data = 'G'; PT.nodes[7].parent = 6;
PT.nodes[8].data = 'H'; PT.nodes[8].parent = 6;
PT.nodes[9].data = 'I'; PT.nodes[9].parent = 6;
CST = CreateCSTreeByPTree(PT);
TreePrintEdge(CST);
return 0;
}
(1)测试实例1:
(2)测试实例2:
【6.11题解答】:
《数据结构题集(C语言版)》,第6章,第44页,第6.63题,难度系数为3。
求树的深度算法:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
typedef struct BiTNode
{
char data;//节点数据
struct BiTNode* lchild;//节点左孩子指针
struct BiTNode* rchild;//节点右孩子指针
struct BiTNode* Parent;
}BiTNode, * BiTree;//二叉树节点
//递归求二叉树深度
int PostTreeDepth(BiTNode* root)
{
int leftdepth, rightdepth, max;
if (root != NULL)
{
leftdepth = PostTreeDepth(root->lchild);
rightdepth = PostTreeDepth(root->rchild);
max = leftdepth > rightdepth ? leftdepth : rightdepth;
return (max + 1);
}
else
return 0;
}
void CreateBiTree(BiTree& T)
{
char ch;
scanf("%c", &ch);
if (' ' == ch)
{
T = NULL;
}
else
{
T = (BiTree)malloc(sizeof(BiTNode));
T->data = ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
int main()
{
int depth;
BiTree T = NULL;
printf("请按前序输入二叉树:\n");
CreateBiTree(T);
depth=PostTreeDepth(T);
printf("二叉树的深度为:%d", depth);
return 0;
}
(1)测试实例1:(测试样例和运行结果截图)
(2)测试实例2:(测试样例和运行结果截图)
【6.12题解答】:
《数据结构题集(C语言版)》,第6章,第44页,第6.68题,难度系数为3。
实现代码:
#define _CRT_SECURE_NO_WARNINGS
/*-------------------
|树-孩子兄弟表达法 |
-------------------*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <stdbool.h>
#ifndef BASE
#define BASE
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define maxSize 100
typedef int Status;
//typedef int bool;
#endif
#define TElemType char
typedef struct CSNode {
TElemType data;
struct CSNode* firstchild, * nextsibling;
}CSNode, * CSTree;
/*-------------------
|6.59 输出T的所有边 |
-------------------*/
void TreePrintEdge(CSTree T) {
CSNode* p;
for (p = T->firstchild; p; p = p->nextsibling) {
printf("(%c,%c)\n", T->data, p->data); //输出T的孩子
TreePrintEdge(p); //输出p的孩子
}
}
/*-------------------------
|6.60 统计叶子结点的个数 |
-------------------------*/
int TreeLeafCnt(CSTree T) {
// 树的叶子结点-->没有孩子
int ret = 0;
CSNode* p;
if (!T) return 0;
else if (!T->firstchild) return 1;
else {
for (p = T->firstchild; p; p = p->nextsibling) ret += TreeLeafCnt(p);
return ret;
}
}
/*-------------------------
|6.61 求树的度 |
-------------------------*/
int TreeDegree(CSTree T) {
// 最大的孩子数
int max = -1;
int cnt = 0;
CSNode* child;
if (!T) return -1; //空树
else if (!T->firstchild) return 0; //只有一个根结点,度为0
else {
for (cnt = 0, child = T->firstchild; child; child = child->nextsibling) cnt++; //求自己的度
max = cnt; //当前的最大值
for (child = T->firstchild; child; child = child->nextsibling) {
cnt = TreeDegree(child);
if (cnt > max) max = cnt;
}
return max;
}
}
/*-------------------------
|6.62 求树的深度 |
-------------------------*/
int TreeDepth(CSTree T) {
int h1, h2;
if (!T) return 0;
else {
h1 = TreeDepth(T->firstchild) + 1; //T孩子的深度+1
h2 = TreeDepth(T->nextsibling); //T兄弟的深度
return h1 > h2 ? h1 : h2;
}
}
/*-----------------------------------------
|6.68 层次序列+每个结点的度-->构造CSTree |
-----------------------------------------*/
CSTree CreateCSTreeByLevelDegree(char* levelstr, int* num) {
int cnt, i, parent;
CSNode* p;
CSNode* tmp[maxSize];
//先创建结点
for (i = 0; i < strlen(levelstr); ++i) {
p = (CSNode*)malloc(sizeof(CSNode)); if (!p) exit(OVERFLOW);
p->data = levelstr[i]; p->firstchild = p->nextsibling = NULL;
tmp[i] = p;
}
//连接
parent = 0; //孩子的爸爸
cnt = 0; //计数器:表示已经找了几个孩子
i = 1; //遍历结点,为他们找爸爸
while (i < strlen(levelstr)) {
if (num[parent] == 0 || cnt == num[parent]) { //这个父亲没有孩子 || parent的孩子已经找完了
cnt = 0; //计数器归0
parent++; //位移一位
continue;
}
//这个父亲有孩子(i是parent的孩子)
cnt++;
if (cnt == 1) { //i是parent的第一个孩子
tmp[parent]->firstchild = tmp[i];
}
else { //不是第一个孩子
tmp[i - 1]->nextsibling = tmp[i]; //它是前面的兄弟
}
i++;
}
return tmp[0];
}
int main() {
/*6.58测试数据
RABCDEFGHI
3 2 0 1 0 0 3 0 0 0
*/
CSTree CST;
char levelstr[50]; //层次遍历的序列
int num[50]; //每个结点的度
int i, cnt;
printf("请输入结点序列\n");
scanf("%s", levelstr);
printf("请输入对应的度\n");
for (i = 0; i < strlen(levelstr); i++) scanf("%d", &num[i]);
CST = CreateCSTreeByLevelDegree(levelstr, num);
TreePrintEdge(CST);
//cnt = TreeLeafCnt(CST); // 叶子结点个数
//printf("TreeLeafCnt:%d\n", cnt);
//cnt = TreeDegree(CST); //树的度
//printf("TreeDegree:%d\n", cnt);
//cnt = TreeDepth(CST); // 树的深度
//printf("TreeDepth:%d\n", cnt);
return 0;
}
测试用例及运行结果截图
【6.13题解答】:
(1) 二叉树先序遍历方式的线索化算法;
#include <stdio.h>
#include <string.h>
typedef struct TBTNode {
char data;
struct TBTNode* lchild;
struct TBTNode* rchild;
unsigned short lTag, rTag;
}TBTNode,*TBTTree;
void prethread(TBTNode* p, TBTNode*& pre) {
if (p != NULL) {
if (p->lchild == NULL) {
p->lchild = pre;
p->lTag = 1;
}
if (pre != NULL && pre->rchild == NULL) {
pre->rchild = p;
pre->rTag = 1;
}
pre = p;
prethread(p->lchild, pre);
prethread(p->rchild, pre);
}
}
(2) 二叉树中序遍历方式的线索化算法;
void inthread(TBTNode* p, TBTNode*& pre) {
if (p != NULL) {
inthread(p->lchild, pre);
if (p->lchild == NULL) {
p->lchild = pre;
p->lTag = 1;
}
if (pre != NULL && pre->rchild == NULL) {
pre->rchild = p;
pre->rTag = 1;
}
pre = p;
inthread(p->rchild, pre);
}
}
(3) 二叉树后序遍历方式的线索化算法;
void postthread(TBTNode* p, TBTNode*& pre) {
if (p != NULL) {
postthread(p->lchild, pre);
postthread(p->rchild, pre);
if (p->lchild == NULL) {
p->lchild = pre;
p->lTag = 1;
}
if (pre != NULL && pre->rchild == NULL) {
pre->rchild = p;
pre->rTag = 1;
}
pre = p;
}
}
(4) 对于先序线索化二叉树,如何求任意一个结点的前驱结点;
如果结点(lTag == 1),直接返回lchild即可
如果结点(lTag != 1){
如果它位于左孩子位置,则父亲结点是前驱
如果它位于右孩子位置{
兄弟结点存在,兄弟结点是它的前驱
兄弟结点不存在,父亲结点是它的前驱
}
}
(5) 对于先序线索化二叉树,如何求任意一个结点的后继结点;
如果结点(RTag == 1),直接返回rchild即可
如果结点(RTag != 1),有左孩子则是左孩子,没有左孩子则是右孩子。
(6) 对于中序线索化二叉树,如何求任意一个结点的前驱结点;
如果结点(LTag == 1),直接返回lchild即可
如果结点(LTag != 1),找它的左子孙中最靠右的那个结点。
(7) 对于中序线索化二叉树,如何求任意一个结点的后继结点;
如果结点(RTag == 1),直接返回rchild即可
如果结点(RTag != 1),找它的右子孙中最靠左的那个结点。
(8) 对于后序线索化二叉树,如何求任意一个结点的前驱结点;
如果结点(LTag == 1),直接返回lchild即可
如果结点(LTag != 1),如果有右儿子,返回右儿子。如果没有右儿子,则返回左儿子
(9) 对于后序线索化二叉树,如何求任意一个结点的后继结点。
如果结点(RTag == 1),直接返回rchild即可
如果结点(RTag != 1){
如果能找到结点的父亲结点且它是右孩子,则是父亲结点
如果能找到p的父节点,且p是左孩子,且右兄弟为空,则是父亲结点。
如果能找到p的父节点,且p是左孩子,且右兄弟非空,则是右孩子。
如果p是根节点,则p没有后序后继。}
【6.14题解答】:
《数据结构题集(C语言版)》,第6章,第41页,第6.26题,难度系数为3。假设用于通信的电文仅由8个字母组成,字母在电文中出现的频率分别为0.07, 0.19, 0.02, 0.06, 0.32, 0.03, 0.21, 0.10。试为这8个字母设计哈夫曼编码。使用0~7的二进制表示形式是另一种编码方案,对于上述实例,比较两种方案的优缺点。
(1)构造的哈夫曼树如图所示
哈夫曼编码如图所示
WPL=(2*5+3*5+6*4+7*4+10*4+32*2+19*2+21*2)*e(-2)=2.61;
(2)二进制编码:
WPL=3*(2+3+6+7+10+32+19+21)*e(-2)=3
优点:
哈夫曼编码的总权值更小,可以节省资源;
二进制编码比较方便。