广州大学学生实验报告
开课学院及实验室: 计算机科学与工程实验室 418 2022年10月3日
学院 | 计算机科学与网络工程 | 年级、专业、班 | 计科 | 姓名 | Great Macro | 学号 | ||
实验课程名称 | 数据结构实验 | 成绩 | ||||||
实验项目名称 | 二叉树的操作与实现 | 指导老师 |
- 实验目的
1.掌握树的定义、相关术语、性质;
2.掌握二叉树的定义、相关性质以及与树的关系;
3. 设计与实现二叉树上的基本操作。
- 使用仪器、器材
1. 微机一台
2. 操作系统:WINXP / wiin8 / win10
3. 编程软件:C / C++ / JAVA
- 实验内容及原理
//描述具体做什么,算法原理、做法
内容:
树是非线性结构,多用链式结构实现。二叉树是结点度不超过2,分支间有左右之分的特殊树。树乃至森林都可以由二叉树唯一地表达。本实验在于加深学生对树或二叉树抽象数据类型有关要素(数据对象、数据关系、以及创建、表示、插入、删除、遍历、查找、深度/宽度计算)的认识(参见《数据结构》教材第5章有关内容)。这里要求定义好树/二叉树的输入表示,至少实现其创建、遍历、深度/宽度计算、相关递归算法等基本操作的计算问题。
原理:
typedef char DataType;//定义数据类型 typedef struct BiTNode {//定义二叉树结构 DataType data; BiTNode* lchild, * rchild; }BiTNode, * BiTree; void CreateBiTree(BiTree& T) {//创建二叉树 DataType ch; cin >> ch; if (ch == '#')T = NULL; else { T = new BiTNode; T->data = ch; CreateBiTree(T->lchild);//创建左子树 CreateBiTree(T->rchild);//创建右子树 } } void PreOrderTraverse(BiTree T) {//先序遍历 if (T) { cout << T->data;//访问结点 PreOrderTraverse(T->lchild);//遍历左子树 PreOrderTraverse(T->rchild);//遍历右子树 } } void PreOrderTraverse_2(BiTree T) { SqStack S; InitStack(S); BiTree p; if (T)Push(S, T); while (!StackEmpty(S)) { Pop(S, p); cout << p->data;//访问结点 if (p->rchild)Push(S, p->rchild);//遍历右子树 if (p->lchild)Push(S, p->lchild);//遍历左子树 } } void PreOrderTraverse_3(BiTree T) { SqStack S; InitStack(S); BiTree p = T; while (p || !StackEmpty(S)) { if (p) {//查找最左子树 cout << p->data;//访问结点 Push(S, p); p = p->lchild; } else {//遍历右子树 Pop(S, p); p = p->rchild; } } } void InOrderTraverse(BiTree T) { //中序遍历 if (T) { InOrderTraverse(T->lchild);//遍历左子树 cout << T->data; //访问结点 InOrderTraverse(T->rchild);//遍历右子树 } } void InOrderTraverse_2(BiTree T) { SqStack S; InitStack(S); BiTree p = T; while (p || !StackEmpty(S)) { if (p) { Push(S, p); p = p->lchild;//遍历左子树 } else { Pop(S, p); cout << p->data;//访问结点 p = p->rchild;//遍历右子树 } } } void PostOrderTraverse(BiTree T) {//后序遍历 if (T) { PostOrderTraverse(T->lchild);//遍历左子树 PostOrderTraverse(T->rchild);//遍历右子树 cout << T->data;//访问结点 } } void PostOrderTraverse_2(BiTree T) { SqStack S; InitStack(S); BiTree p = T; BiTree q = NULL; while (p || !StackEmpty(S)) { if (p) { Push(S, p); p = p->lchild;//遍历最左子树 } else { p = GetTop(S); if (p->rchild && p->rchild != q) { p = p->rchild; } else { Pop(S, p); cout << p->data;//访问结点 q = p; p = NULL; } } } } void LevelOrderTraverse(BiTree T) { SQueue Q; InitQueue(Q); if (T)EnQueue(Q,T); while (!QueueEmpty(Q)) { BiTree t=GetHead(Q); DeQueue(Q,t); cout << t->data;//访问结点 if (t->lchild)EnQueue(Q,t->lchild);//左孩子进队 if(t->rchild)EnQueue(Q, t->rchild);//右孩子进队 } DestroyQueue(Q);//释放空间 } void BiTreeCopy(BiTree T, BiTree& NewT) {//复制二叉树 if (T == NULL) NewT = NULL; else { NewT = new BiTNode; NewT->data = T->data;//复制结点 BiTreeCopy(T->lchild, NewT->lchild);//复制左子树 BiTreeCopy(T->rchild, NewT->rchild);//复制右子树 } } int BiTreeDepth(BiTree T) {//计算二叉树的深度 if (!T)return 0; else { int m = BiTreeDepth(T->lchild);//左子树深度 int n = BiTreeDepth(T->rchild);//右子树深度 if (m > n)return m + 1;//返回最大的深度 else return n + 1; } } int BiTreeWidth(BiTree T) {//计算二叉树的宽度 if(!T)return 0; else { int n= BiTreeDepth(T); int* num = new int[n];//给每一层分配一个空间记录该层结点数目 for (int i = 0; i < n; i++) num[i] = 0;//初始化每层的结点数目为0 SQueue Q; InitQueue(Q); if (T) { EnQueue(Q, T);}; for (int j = 0; j < n && !QueueEmpty(Q); j++) { num[j]= QueueLength(Q);//记录每层的结点数目 for (int i = 0; i < num[j]; i++) {//弹出上一层所有结点,并将下一层所有结点进队 BiTree p=GetHead(Q); DeQueue(Q, p); if (p->lchild)EnQueue(Q, p->lchild);//左孩子进队 if (p->rchild)EnQueue(Q, p->rchild);//右孩子进队 } } int max = num[0]; for (int i = 1; i < n; i++) {//比较最大宽度 if (num[i] > max)max = num[i]; } delete []num; return max; } } int NodeCount(BiTree T) {//计算二叉树中的结点的个数 if (T == NULL)return 0; else return NodeCount(T->lchild) + NodeCount(T->rchild) + 1; } int NodeCount_0(BiTree T) {//计算二叉树中的叶结点 if (T == NULL)return 0; else if (!T->lchild && !T->rchild)return 1; else return NodeCount_0(T->lchild) + NodeCount_0(T->rchild); } int NodeCount_1(BiTree T) { // 计算二叉树中度数为1的结点数目 if (T == NULL)return 0; if (!T->lchild&& T->rchild || T->lchild&&!T->rchild) return NodeCount_1(T->lchild) + NodeCount_1(T->rchild) + 1; else return NodeCount_1(T->lchild) + NodeCount_1(T->rchild); } int NodeCount_2(BiTree T) { // 计算二叉树中度数为2的结点数目 if (T == NULL)return 0; if (T->lchild && T->rchild)return NodeCount_2(T->lchild) + NodeCount_2(T->rchild)+1; else return NodeCount_2(T->lchild) + NodeCount_2(T->rchild); } DataType BiTreeRoot(BiTree T) {//返回根结点 if (!T)return '0'; return T->data; } bool DestroyBiTree(BiTree& T) { if (T) { DestroyBiTree(T->lchild);//删除左子树 DestroyBiTree(T->rchild);//删除右子树 delete T; return true; } else return false; } bool ClearBiTree(BiTree &T) { if (T) { ClearBiTree(T->lchild);//清空左子树 ClearBiTree(T->rchild);//清空右子树 T = NULL; return true; } return false; } bool CheckBiTree(BiTree T,DataType data) { if (!T)return false; else { if (T->data == data) {//查找到结点 return true; }//查找左子树和右子树 return CheckBiTree(T->lchild, data) || CheckBiTree(T->rchild, data); } } bool BiTreeEmpty(BiTree T) {//判断二叉树是否为空 if (!T)return true; else return false; } int InsertChild(BiTree& T, DataType parentdata, DataType newdata,int LR) { if (!T)return 0; else { if (T->data == parentdata) { BiTNode* child = new BiTNode; child->data = newdata; child->lchild = NULL; if (LR == 0) {//插入左子树 BiTNode* q = T->lchild; child->rchild = q; T->lchild = child; } else {//插入右子树 BiTNode* s = T->rchild; child->rchild = s; T->rchild = child; } return 1; }//查找左右子树并插入 return InsertChild(T->lchild, parentdata, newdata, LR) + InsertChild(T->rchild, parentdata, newdata, LR); } } int DeleteChild(BiTree& T, DataType parentdata, int LR) { if (!T)return 0; else { if (T->data == parentdata) {//查找双亲结点 if (LR == 0) { DestroyBiTree(T->lchild);//删除左子树 T->lchild = NULL; } else { DestroyBiTree(T->rchild);//删除右子树 T->rchild = NULL; } return 1; } return DeleteChild(T->lchild, parentdata, LR)+DeleteChild(T->rchild,parentdata,LR); } }
哈夫曼树与编码:
typedef struct { int weight; int parent, lchild, rchild; }HTNode,*HuffmanTree; void Select(HuffmanTree HT, int n, int& s1, int& s2) { if (n <= 1)return; int i; int k = 0; int* w= new int[n+1]; for (i = 0; i < n; i++) w[i] = 0; for (i = 1; i <= n; i++) { if (!HT[i].parent ) w[k++]=i;//w[k]为双亲为0的结点序号 } if (w[0] || w[1]) { s1 = w[0]; for (i = 1; i < k; i++) if (HT[w[i]].weight < HT[s1].weight)s1 = w[i];//选择最小的 for (i = 0; i < k; i++) if (w[i] != s1) { s2 = w[i]; break; } for (i = 0; i < k; i++) if (HT[w[i]].weight < HT[s2].weight && w[i]!= s1)s2 = w[i];//选择第二小的 } delete[]w; } void CreateHuffmanTree(HuffmanTree& HT, int n) { if (n <= 1)return; int m = 2 * n - 1; HT = new HTNode[m + 1]; for (int i = 1; i <= m; i++) {//初始化 HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0; } for (int i = 1; i <= n; i++) { cin >> HT[i].weight; } cout << "构造哈夫曼树:" << endl; for (int i = n + 1; i <= m; i++) {//选择剩下的结点 int s1 = 0; int s2 = 0; Select(HT, i - 1, s1, s2); HT[s1].parent = i; HT[s2].parent = i; HT[i].lchild = s1; HT[i].rchild = s2; HT[i].weight = HT[s1].weight + HT[s2].weight; cout<<"原来的两个结点:"<< HT[s1].weight << " " << HT[s2].weight <<" 新的结点:"<<HT[i].weight<<endl; } } void Copy(char *HC, int start, int end, char* cd) {//复制编码 for (int i = 0;i<end-start-1; i++) HC[i] = cd[start+i]; HC[end - start-1] = '\0'; } typedef char** HuffmanCode; void CreateHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n) { HC = new char* [n + 1]; char *cd = new char[n]; cd[n - 1] = '\0'; for (int i = 1; i <= n; i++) {//向上回溯 int start = n - 1; int c = i; int f = HT[i].parent; while (f != 0) { --start; if (HT[f].lchild == c)cd[start] = '0';//左孩子为0 else cd[start] = '1';//右孩子为1 c = f; f = HT[f].parent; } HC[i] = new char[n - start]; Copy(HC[i], start, n, cd); } delete[]cd; }
- 实验过程原始数据记录
//代码及调试信息、截屏说明
测试函数:
void test(BiTree& T) { cout << "请选择对二叉树的操作:" << endl; cout << "A.创建 B.遍历 C.插入 D.删除 E.查树 F.查根 G.清空 H.销毁 I.退出 " << endl; char choice; int key = 0; cin >> choice; while (1) { if (choice == 'A') { key = 1; cout << "采用先序遍历的顺序建立二叉链表,以#结束输入 " << endl;; CreateBiTree(T); cout << "创建成功!" << endl; } if (choice == 'I') { cout << "你已退出!" << endl; break; } if (!key && choice != 'H') { cout << "该树不存在!请先创建一个树!" << endl; } cout << "请选择对二叉树的操作:" << endl; cout << "A.创建 B.遍历 C.插入 D.删除 E.查树 F.查根 G.清空 H.销毁 I.退出 " << endl; cin >> choice; if (choice < 'A' || choice>'I') { cout << "输入不合法!请重新输入:"; cin >> choice; } if (key) { switch (choice) { case 'B': if (BiTreeEmpty(T))cout << "这是一棵空树!" << endl; else { cout << "请选择遍历方式:a.先序遍历 b.中序遍历 c.后序遍历 d.层次遍历" << endl; cin >> choice; switch (choice) { case 'a':cout << "先序遍历结果:"; PreOrderTraverse_3(T); break; case 'b':cout << "中序遍历结果:"; InOrderTraverse_2(T); break; case 'c':cout << "后序遍历结果:"; PostOrderTraverse(T); break; case 'd':cout << "层次遍历结果:"; LevelOrderTraverse(T); break; } cout << endl; } break; case 'C': if (BiTreeEmpty(T))cout << "这是一棵空树!" << endl; else { cout << "请输入想插入的结点的双亲:"; DataType a; cin >> a; if (CheckBiTree(T, a)) { cout << "请输入插入的结点的元素:"; DataType b; cin >> b; cout << "请选择插入结点的类型:0.左孩子 1.右孩子: "; int LR; cin >> LR; InsertChild(T, a,b, LR); cout << "插入成功!" << endl; } else cout << "二叉树中不存在该结点!" << endl; } break; case 'D': if (BiTreeEmpty(T))cout << "这是一棵空树!" << endl; else { cout << "请输入想删除子树的双亲:"; DataType c; cin >> c; if (CheckBiTree(T, c)) { cout << "请选择删除结点的子树:0.左子树 1.右子树: "; int LR; cin >> LR; DeleteChild(T, c, LR); cout << "删除成功!" << endl; } } break; case 'E': if (BiTreeEmpty(T))cout << "这是一棵空树!" << endl; else { cout << "请选择查找的操作:a.宽度 b.深度 c.结点数目 d.叶结点数目 e.度数为1的结点的数目 f.度数为2的结点的数目" << endl; cin >> choice; switch (choice) { case 'a':cout << "该二叉树的宽度是: " << BiTreeWidth(T) << endl; break; case 'b':cout << "该二叉树的深度是: " << BiTreeDepth(T) << endl; break; case 'c':cout << "该二叉树的结点数目:" << NodeCount(T) << endl; break; case 'd':cout << "该二叉树的叶结点数目:" << NodeCount_0(T) << endl; break; case 'e':cout << "该二叉树度数为1的结点数目:" << NodeCount_1(T) << endl; break; case 'f':cout << "该二叉树度数为2的结点数目:" << NodeCount_2(T) << endl; break; } break; case 'F': if (BiTreeEmpty(T))cout << "这是一棵空树!" << endl; else cout << "该二叉树的根结点是: " << BiTreeRoot(T) << endl; break; case 'G': if (BiTreeEmpty(T))cout << "这是一棵空树!" << endl; else { ClearBiTree(T); cout << "清空成功!" << endl; } break; case 'H': DestroyBiTree(T); key = 0; cout << "销毁成功!" << endl; break; } } } } } int main() { BiTree T=NULL; test(T); }
按照测试函数逐个测试功能:
创建一颗二叉树如图:
遍历该二叉树
查找该二叉树的结点信息
在根节点插入右子树K,如图:
最后测试清空和销毁功能:
哈夫曼编码的测试函数:
int main() { HuffmanTree HT; HuffmanCode HC; cout << "请输入哈夫曼树的结点数目:"; int n; cin >> n; cout << "请输入结点的值:" << endl; CreateHuffmanTree(HT, n); CreateHuffmanCode(HT, HC, n); cout << "进行哈夫曼编码:" << endl; for (int i = 1; i <= n; i++) { cout <<"第"<<i<<"个结点编码:" << HC[i] << endl; } }
以(5,29 7,8,14,23,3,11)构造一颗哈夫曼树:
对应编码:
5: 0001
29:10
7: 1110
8: 1111
14:110
23:01
3: 0000
11:00