一、实验目的:
1、二叉树的基本操作算法实现
2、二叉树的各种遍历算法实现
3、线索二叉树的遍历
4、构造哈夫曼树和哈夫曼编码的算法实现
二、使用仪器、器材
微机一台
操作系统:Win7及以上
编程软件:C/C++
三、实验内容及原理
利用二叉树的二叉链式存储结构设计并实现各种操作算法。
1、二叉树的基本操作算法实现
(1)利用二叉树字符串“A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))”创建二叉树的二叉链式存储结构;
(2)输出该二叉树;
(3)输出‘H’节点的左、右孩子结点值;
(4)输出该二叉树的结点个数、叶子结点个数、二叉树的度和高度;
2、二叉树的各种遍历算法实现
实现上述二叉树的先序、中序和后序遍历的递归和非递归算法;
3、线索二叉树的遍历
中序线索化上述二叉树并找出根结点的前驱和后继。
4、构造哈夫曼树和哈夫曼编码的算法实现
统计下面一段英文的不同字符个数和每个字符的出现频率,利用统计数据构造构造哈夫曼树和哈夫曼编码
The Chinese official said he viewed the Trump Presidency not as an aberration but as the product of a failing political system. This jibes with other accounts. The Chinese leadership believes that the United States, and Western democracies in general, have n0t risen to the challenge of a globalized economy, which necessitates big changes in production patterns, as well as major upgrades in education and public infrastructure. In Trump and Trumpism, the Chinese see an inevitable backlash to this failure.
四、实验过程原始数据记录
截屏及解读
- 二叉树的基本操作算法实现
- 二叉树的各种遍历算法实现
- 线索二叉树的遍历
-
#include<iostream> #include<stack> using namespace std; //创建结点 struct BiTNode { char data; struct BiTNode* lchild, * rchild; int lTag, rTag; //线索 }; //创建树 void createBiTree(BiTNode*& T) { BiTNode* S[50]; BiTNode* p = NULL; int top = 0, k = 0; T = NULL; char ch; cin >> ch; while (ch != '#') { switch (ch) { case '(': { S[++top] = p; k = 1; break; } case ')': top--; break; case ',': k = 2; break; default: { p = new BiTNode; p->data = ch; p->lchild = p->rchild = NULL; if (T == NULL) T = p; else { switch (k) { case 1: S[top]->lchild = p; break; case 2: S[top]->rchild = p; break; } } break; } } cin >> ch; } } //创建有数据域的结点 struct BiTNode* createNode(char data) { struct BiTNode* newNode = new BiTNode; newNode->data = data; newNode->lchild = NULL; newNode->rchild = NULL; return newNode; } //输出二叉树 void printTree(struct BiTNode* headNode) { cout << headNode->data; if (headNode->lchild != NULL && headNode->rchild != NULL) { cout << '('; printTree(headNode->lchild); cout << ","; printTree(headNode->rchild); cout << ')'; } else if (headNode->lchild != NULL && headNode->rchild == NULL) { cout << '('; printTree(headNode->lchild); cout << ')'; } else if (headNode->lchild == NULL && headNode->rchild != NULL) { cout << "(,"; printTree(headNode->rchild); cout << ')'; } } //输出某结点的左右孩子结点值 void printChild(struct BiTNode* headNode, char data) { if (headNode->data == data) { if (headNode->lchild != NULL && headNode->rchild != NULL) { cout << "左孩子为:" << headNode->lchild->data << " " << ",右孩子为:" << headNode->rchild->data << endl; return; } else if (headNode->lchild != NULL && headNode->rchild == NULL) { cout << "左孩子为:" << headNode->lchild->data << ",右孩子为空" << endl; return; } else if (headNode->lchild == NULL && headNode->rchild != NULL) { cout << "右孩子为空,左孩子为:" << headNode->rchild->data << endl; return; } else { cout << "左右孩子都为空" << endl; return; } } else { if (headNode->lchild != NULL) printChild(headNode->lchild, data); if (headNode->rchild != NULL) printChild(headNode->rchild, data); } } //二叉树的结点个数 int countNode(struct BiTNode* headNode) { if (headNode == NULL) return 0; else return 1 + countNode(headNode->lchild) + countNode(headNode->rchild); } //叶结点个数 static int leavesnum = 0; void countleaves(struct BiTNode* headNode) { if (headNode->lchild == NULL && headNode->rchild == NULL) leavesnum++; else { if (headNode->lchild != NULL) countleaves(headNode->lchild); if (headNode->rchild != NULL) countleaves(headNode->rchild); } } //二叉树的度 int deg = 0; void Degree(struct BiTNode* headNode) { if (headNode->lchild != NULL && headNode->rchild != NULL) { deg = 2; return; } else if (headNode->lchild != NULL && headNode->rchild == NULL) { deg = 1; Degree(headNode->lchild); } else if (headNode->lchild == NULL && headNode->rchild != NULL) { deg = 1; Degree(headNode->rchild); } } //二叉树的高度 int Height(struct BiTNode* headNode) { //二叉树的高度 = 1 + max(左子树高度,右子树高度) if (headNode == NULL) return 0;//如果为空指针,返回0 else { if (Height(headNode->lchild) > Height(headNode->rchild)) return 1 + Height(headNode->lchild); return 1 + Height(headNode->rchild); } } //先序遍历(递归 void preOrder(struct BiTNode* headNode) { if (headNode == NULL) return; cout << headNode->data; preOrder(headNode->lchild); preOrder(headNode->rchild); } //先序遍历(非递归 void preOrder1(struct BiTNode* headNode) { if (headNode == NULL) //空树 return; stack<struct BiTNode*> stackNode; //创建一个栈保存结点 stackNode.push(headNode); //根结点进栈 while (!stackNode.empty()) //栈非空时迭代 { struct BiTNode* node = stackNode.top(); //保存栈顶结点 cout << node->data; //访问结点数据 stackNode.pop(); //栈顶结点出栈 //孩子结点入栈, 右结点先进后出 if (node->rchild) stackNode.push(node->rchild); if (node->lchild) stackNode.push(node->lchild); } } //中序遍历(递归 void inOrder(struct BiTNode* headNode) { if (headNode == NULL) return; inOrder(headNode->lchild); cout << headNode->data; inOrder(headNode->rchild); } //中序遍历(非递归 void inOrder1(struct BiTNode* headNode) { if (headNode == NULL) //空树 return; stack<struct BiTNode*> stackNode; //创建一个栈保存结点 struct BiTNode* currentNode = headNode; //记录当前结点的指针 //当前结点或栈非空时迭代 while (currentNode || !stackNode.empty()) { //当前结点非空,沿着左子树方向入栈 while (currentNode) { stackNode.push(currentNode); currentNode = currentNode->lchild; } //此时栈顶元素没有左子树,或者已经访问完左子树 currentNode = stackNode.top(); //取栈顶结点 cout << currentNode->data; stackNode.pop(); //出栈 currentNode = currentNode->rchild; //将当前结点设为右子树结点 } } //后序遍历(递归 void postOrder(struct BiTNode* headNode) { if (headNode == NULL) return; postOrder(headNode->lchild); postOrder(headNode->rchild); cout << headNode->data; } //后序遍历(非递归 void postOrder1(struct BiTNode* headNode) { if (headNode == NULL) return; stack<BiTNode*> stackNode; BiTNode* currentNode = headNode; BiTNode* lastNode = headNode; //上一个结点 while (currentNode || !stackNode.empty()) { while (currentNode) { stackNode.push(currentNode); currentNode = currentNode->lchild; } currentNode = stackNode.top(); if (currentNode->rchild && currentNode->rchild != lastNode) currentNode = currentNode->rchild; else { cout << currentNode->data; lastNode = currentNode; currentNode = NULL; stackNode.pop(); } } } //中序线索化二叉树 struct BiTNode* pre = new BiTNode; void inThreading(struct BiTNode* p) { if (p) { inThreading(p->lchild); if (!p->lchild) { p->lTag = 1; p->lchild = pre; } else p->lTag = 0; if (!pre->rchild) { pre->rTag = 1; pre->rchild = p; } else pre->rTag = 0; pre = p; inThreading(p->rchild); } } //中序线索化找结点的前驱和后继 void inPreAndNext(struct BiTNode* p) { struct BiTNode* q, * temp = new struct BiTNode; //前驱 if (p->lTag == 1) temp = p->lchild; else for (q = p->lchild; q->rTag == 0; q = q->rchild) temp = q; cout << "该结点的前驱为:" << temp->data << endl; //后继 if (p->rTag == 1) temp = p->rchild; else for (q = p->rchild; q->lTag == 0; q = q->lchild) temp = q; cout << "该结点的后继为:" << temp->data << endl; } int main() { //初始化二叉树 /*struct BiTNode* headNode = createNode('A'); headNode->lchild = createNode('B'); headNode->lchild->lchild = createNode('D'); headNode->lchild->rchild = createNode('E'); headNode->lchild->rchild->lchild = createNode('H'); headNode->lchild->rchild->lchild->lchild = createNode('J'); headNode->lchild->rchild->lchild->rchild = createNode('K'); headNode->lchild->rchild->lchild->rchild->lchild = createNode('L'); headNode->lchild->rchild->lchild->rchild->rchild = createNode('M'); headNode->lchild->rchild->lchild->rchild->rchild->rchild = createNode('N'); headNode->rchild = createNode('C'); headNode->rchild->lchild = createNode('F'); headNode->rchild->rchild = createNode('G'); headNode->rchild->rchild->rchild = createNode('I');*/ struct BiTNode* headNode; cout << "请输入二叉树:(请以#结束)" << endl; createBiTree(headNode); //输出该二叉树 cout << " 该二叉树为:" << endl; printTree(headNode); cout << endl; //输出某结点的左右孩子结点值 cout << "\n H结点:" << endl; printChild(headNode, 'H'); //二叉树的结点个数 cout << "\n该二叉树的结点个数为:" << countNode(headNode) << endl; //叶结点个数 countleaves(headNode); cout << "\n该二叉树的叶子结点个数为:" << leavesnum << endl; //二叉树的度 Degree(headNode); cout << "\n该二叉树的度为:" << deg << endl; //二叉树的高度 cout << "\n该二叉树的高度为:" << Height(headNode) << endl; cout << "\n二叉树的先序遍历(递归算法):" << endl; preOrder(headNode); cout << endl; cout << "\n二叉树的先序遍历(非递归算法):" << endl; preOrder1(headNode); cout << endl; cout << "\n二叉树的中序遍历(递归算法):" << endl; inOrder(headNode); cout << endl; cout << "\n二叉树的中序遍历(非递归算法):" << endl; inOrder1(headNode); cout << endl; cout << "\n二叉树的后序遍历(递归算法):" << endl; postOrder(headNode); cout << endl; cout << "\n二叉树的后序遍历(非递归算法):" << endl; postOrder1(headNode); cout << endl << endl; inThreading(headNode); inPreAndNext(headNode); return 0; }
- 构造哈夫曼树和哈夫曼编码的算法实现
#include<iostream>
#include<iomanip>
#include<fstream>
using namespace std;
typedef struct
{
int weight;
int parent, lchild, rchild;
char info;
}HuNode, * HuTree;
struct CH
{
char data;
int count;
}Ch[100];
bool Judge(char temp, int j)//判断是否重复
{
for (int i = 0; i < j; i++)
if (Ch[i].data == temp)
return true; //重复
return false;
}
int GetCount()//写入每种字符及其出现频率
{
fstream file;
char ch;
file.open("text.txt", ios::in);
int j = 0, sum = 0;
while(file && file.get(ch))
{
sum++;
char temp = ch;
if (!Judge(temp, j))
{
Ch[j].data = temp;
Ch[j].count = 1;
j++;
}
else
{
for (int k = 0; k < j; k++)
if (Ch[k].data == temp)
Ch[k].count++;
}
}
file.close();
cout << "总字符数为:" << sum << endl;
for (int i = 0; i < j; i++)
{
cout << Ch[i].data << ":" << Ch[i].count << '\t';
if ((i + 1) % 10 == 0)
cout << endl;
}
return j;
}
int Min_index(HuTree& HT, int k)
{
int i, min = 0;
int minweight = 32767;
for (i = 1; i <= k; i++)
{
if (HT[i].parent == 0 && HT[i].weight < minweight)
{
min = i;
minweight = HT[i].weight;
}
}
HT[min].parent = k + 1;
cout << "min=" << min << "\tk=" << k << "\tparent=" << HT[min].parent << endl;
return min;
}
void Select(HuTree HT, int x, int& s1, int& s2)
{
s1 = Min_index(HT, x - 1);
s2 = Min_index(HT, x - 1);
}
void createHuTree(HuTree& HT, int n)
{
int m, i, s1, s2;
if (n <= 1)
return;
m = 2 * n - 1;
HT = new HuNode[m + 1];
for (int i = 1; i <= m; i++)
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
for (i = 1; i <= n; i++)
{
HT[i].weight = Ch[i].count;
HT[i].info = Ch[i].data;
}
cout << "\n\n哈夫曼树结点的合并过程" << endl;
for (i = n + 1; i <= m; i++)
{
Select(HT, i, s1, s2);
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
}
}
void CreatHuffmanCode(HuNode* HT, char**& HC, int n) //结点,可变长的字符串数组,字母个数
{
char* code;
int i, c, f, start;
HC = new char* [n + 1]; //分配存储n个字符编码的编码表空间
code = new char[n]; //分配临时存放每个字符编码的动态数组空间
code[n - 1] = '\0';
for (i = 1; i <= n; i++)
{
start = n - 1;
c = i;
f = HT[i].parent;
while (f != 0)
{
--start;
if (HT[f].lchild == c)
code[start] = '0';
else
code[start] = '1';
c = f;
f = HT[f].parent;
}
HC[i] = new char[n - start];
strcpy(HC[i], &code[start]);
}
delete code;
}
int main()
{
HuTree HT;
char** HC;
int i, m, n = 29;
m = 2 * n - 1;
cout << "读取txt文件,并对英文字符出现的次数进行统计" << endl;
cout << "\n字符的种类有:" << GetCount() << "种" << "\n";
createHuTree(HT, n);
cout << "\n哈夫曼数组" << endl;
cout << "下标" << "\t" << "结点" << "\t" << "权重" << "\t" << "双亲" << "\t" << "左孩子" << "\t" << "右孩子" << "\t" << endl;
for (i = 1; i <= m; i++)
cout << i << "\t" << HT[i].info << "\t" << HT[i].weight << "\t" << HT[i].parent << "\t" << HT[i].lchild << "\t" << HT[i].rchild << "\t" << endl;
CreatHuffmanCode(HT, HC, n);
cout << "\n 该文件中的字母的哈夫曼编码为:" << endl;
for (int i = 1; i <= 26; i++)
cout << HT[i].info << ": " << HC[i] << endl;
return 0;
}
- 实验结果及分析
哈夫曼刚开始检测字符时,只检测了26个字母,并将大小写归为同一个,存在字母表数组中,但是转念一想,编码后解码解出原文会比较费劲,然后就将所有的字符都重新编码了。
- 总结
这次实验,我对二叉树和哈夫曼树有了更好的认识。在实验过程中,我掌握了哈夫曼树的构造方法,学会了如何将理论知识转换成实际应用。同时,在解决程序中遇到的一些问题的同时,我也对调试的技巧有了更好的掌握,分析问题的能力也有提高。