记录实验,同时也是记录常见数据结构算法的实现。
广州大学学生实验报告
开课实验室:计算机科学与工程实验(电子楼416A)
学院 计算机科学与网络工程学院
实验课程 数据结构实验
实验项目 实验二 二叉树的操作与实现
#include
using namespace std;
int leave_counts = 0;
//-----二叉树的二叉链表表示-----
typedef struct BiTNode
{
char data;
BiTNode* lchild, * rchild;
}BiTNode, *BiTree;
//-----先序遍历建立二叉树-----
void CreatBiTNode(BiTree& T,const char *str)
{
BiTNode* ParentTNode[20], * p = NULL;
int top = -1, k=0, j = 0;
char ch;
T = NULL;
ch = str[j];
while (ch != '\0')
{
switch (ch)
{
case'(':top++; ParentTNode[top] = p; k = 1; break;
case')':top--; break;
case',':k = 2; break;
default: p = new BiTNode;
p->data = ch;
p->lchild = NULL;
p->rchild = NULL;
if (T == NULL) T = p;
switch (k)
{
case 1:ParentTNode[top]->lchild = p; break;
case 2:ParentTNode[top]->rchild = p; break;
}
}
j++;
ch = str[j];
}
}
//-----查找某节点-----
BiTNode* FindTNodeChild(BiTree T, char ch)
{
BiTNode* p;
if (T == NULL) return NULL;
else if (T->data == ch)
return T;
else
{
p = FindTNodeChild(T->lchild, ch);
if ( p!= NULL)
return p;
else
return FindTNodeChild(T->rchild, ch);
}
}
//-----先序遍历输出二叉树-----
void ShowBiTree(BiTree T)
{
if (T)
{
cout << T->data;
ShowBiTree(T->lchild);
ShowBiTree(T->rchild);
}
}
//-----输出二叉树的结点个数-----
int NodeCount(BiTree &T)
{
if (T == NULL) return 0;
else return NodeCount(T->lchild) + NodeCount(T->rchild)+1;
}
//-----输出二叉树的度-----
int DegreeCount(BiTree T)
{
return NodeCount(T) - 1;
}
//-----输出二叉树的叶子数-----
int LeaveCount(BiTree T)
{
if (T != NULL)
{
if (T->lchild == NULL && T->rchild == NULL)
leave_counts ++;
LeaveCount(T->lchild);
LeaveCount(T->rchild);
return leave_counts;
}
}
//-----输出二叉树的深度-----
int DepthOfTree(BiTree T)
{
if (T == NULL) return 0;
else
{
int depth_l = DepthOfTree(T->lchild);
int depth_r = DepthOfTree(T->rchild);
if (depth_l <= depth_r) return depth_r + 1;
else return depth_l + 1;
}
}
int main()
{
BiTree T = new BiTNode;
const char* ch = "A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))\0";
CreatBiTNode(T,ch);
//先序遍历输出
cout << "先序遍历输出:";
ShowBiTree(T);
//输出A的左右孩子
BiTNode* p = FindTNodeChild(T,'A');
if (p->lchild != NULL && p->rchild != NULL) cout << "\n结点A的左孩子:" << p->lchild->data << "\t右孩子:" << p->rchild->data<<endl;
else if (p->lchild != NULL && p->rchild == NULL)cout << "结点A的左孩子:" << p->lchild->data;
else if(p->lchild == NULL && p->rchild != NULL) cout << "结点A的右孩子:" << p->lchild->data;
//结点数
cout << "结点数: " << NodeCount(T) << endl;
//叶子数
cout << "叶子数: " << LeaveCount(T) << endl;
//度
cout << "度: " << DegreeCount(T) << endl;
//深度
cout << "深度" << DepthOfTree(T) << endl;
}
2、二叉树的各种遍历算法实现 (返回目录)
#include
using namespace std;
int leave_counts = 0;
//-----二叉树的二叉链表表示-----
typedef struct BiTNode
{
char data;
BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
//-----用栈来实现二叉链表的非递归-----
typedef struct BiTNodeStack
{
BiTNode* data;
BiTNodeStack* next;
}BiTNodeStack,*BiTStack;
//-----初始化栈-----
void InitStack(BiTStack& s)
{
s = new BiTNodeStack;
s->data = NULL;
s->next = NULL;
}
//-----弹出-----
BiTNode* Pop(BiTStack s)
{
BiTNodeStack* p = new BiTNodeStack;
p = s->next;
s->next = p->next;
return p->data;
}
//-----放入-----
void Push(BiTStack &s,BiTNode *p)
{
BiTNodeStack* sp = new BiTNodeStack;
sp->data = p;
sp->next = s->next;
s->next = sp;
}
//-----先序遍历建立二叉树-----
void CreatBiTNode(BiTree& T, const char* str)
{
BiTNode* ParentTNode[20], * p = NULL;
int top = -1, k = 0, j = 0;
char ch;
T = NULL;
ch = str[j];
while (ch != '\0')
{
switch (ch)
{
case'(':top++; ParentTNode[top] = p; k = 1; break;
case')':top--; break;
case',':k = 2; break;
default: p = new BiTNode;
p->data = ch;
p->lchild = NULL;
p->rchild = NULL;
if (T == NULL) T = p;
switch (k)
{
case 1:ParentTNode[top]->lchild = p; break;
case 2:ParentTNode[top]->rchild = p; break;
}
}
j++;
ch = str[j];
}
}
//-----递归先序遍历输出二叉树-----
void ShowBiTree_preorder(BiTree T)
{
if (T)
{
cout << T->data;
ShowBiTree_preorder(T->lchild);
ShowBiTree_preorder(T->rchild);
}
}
//-----非递归先序遍历输出二叉树-----
void ShowBiTree_preorder_0(BiTree T)
{
BiTNode* p = new BiTNode;
p = T;
BiTStack s;
InitStack(s);
while (p || s->next != NULL)
{
while (p)
{
cout << p->data;
Push(s, p);
p = p->lchild;
}
if (s->next != NULL)
{
p = Pop(s);
p = p->rchild;
}
}
}
//-----递归中序遍历输出二叉树-----
void ShowBiTree_inorder(BiTree T)
{
if (T)
{
ShowBiTree_inorder(T->lchild);
cout << T->data;
ShowBiTree_inorder(T->rchild);
}
}
//-----非递归中序遍历输出二叉树-----
void ShowBiTree_inorder_0(BiTree T)
{
BiTNode* p = new BiTNode;
p = T;
BiTStack s ;
InitStack(s);
while (p || s->next != NULL)
{
if (p)
{
Push(s, p);
p = p->lchild;
}
else
{
BiTNode* q = new BiTNode;
q = Pop(s);
cout << q->data;
p = q->rchild;
}
}
}
//-----递归后序遍历输出二叉树-----
void ShowBiTree_postorder(BiTree T)
{
if (T)
{
ShowBiTree_postorder(T->lchild);
ShowBiTree_postorder(T->rchild);
cout << T->data;
}
}
//-----非递归后序遍历输出二叉树-----
void ShowBiTree_postorder_0(BiTree T)
{
BiTNode* p = new BiTNode;
p = T;
BiTStack s1,s2;
InitStack(s1);
InitStack(s2);
while (p || s1->next != NULL)
{
while (p)
{
Push(s2, p);
Push(s1, p);
p = p->rchild;
}
if (s1->next != NULL)
{
p = Pop(s1);
p = p->lchild;
}
}
while (s2->next != NULL)
{
p = Pop(s2);
cout << p->data;
}
}
int main()
{
BiTree T = new BiTNode;
const char* ch = "A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))\0";
CreatBiTNode(T, ch);
//递归先序遍历输出
cout << "先序遍历输出:";
ShowBiTree_preorder(T);
cout <<" (递归)"<<endl;
//非递归先序遍历输出
cout << "先序遍历输出:";
ShowBiTree_preorder_0(T);
cout << " (非递归)" << endl<<"----------------"<<endl;
//递归中序遍历输出
cout << "中序遍历输出:";
ShowBiTree_inorder(T);
cout << " (递归)" << endl;
//非递归中序遍历输出
cout << "中序遍历输出:";
ShowBiTree_inorder_0(T);
cout << " (非递归)" << endl << "----------------" << endl;
//递归后序遍历输出
cout << "后序遍历输出:";
ShowBiTree_postorder(T);
cout << " (递归)" << endl;
//非递归后序遍历输出
cout << "后序遍历输出:";
ShowBiTree_postorder_0(T);
cout << " (非递归)" << endl;
}
3、线索二叉树的遍历 (返回目录)
#include
using namespace std;
//-----线索二叉树-----
typedef struct BiThrNode
{
char data;
BiThrNode* lchild, * rchild;
int LTag, RTag;
}BiThrNode,*BiThrTree;
BiThrNode* pre = new BiThrNode; // 全局变量pre
//-----先序遍历建立二叉树-----
void CreatBiTNode(BiThrTree& T, const char* str)
{
BiThrNode* ParentTNode[20], * p = NULL;
int top = -1, k = 0, j = 0;
char ch;
T = NULL;
ch = str[j];
while (ch != '\0')
{
switch (ch)
{
case'(':top++; ParentTNode[top] = p; k = 1; break;
case')':top--; break;
case',':k = 2; break;
default: p = new BiThrNode;
p->data = ch;
p->lchild = NULL;
p->rchild = NULL;
if (T == NULL) T = p;
switch (k)
{
case 1:ParentTNode[top]->lchild = p; break;
case 2:ParentTNode[top]->rchild = p; break;
}
}
j++;
ch = str[j];
}
}
//-----中序线索化二叉树-----
void InThreading(BiThrTree p)
{
pre->LTag = 0;
pre->RTag = 0;
pre->rchild = NULL;
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 InOrderThreading(BiThrTree& Thrt, BiThrTree T)
{
Thrt = new BiThrNode;
Thrt->LTag = 0;
Thrt->RTag = 1;
Thrt->rchild = Thrt;
if (!T) Thrt->lchild = Thrt;
else
{
Thrt->lchild = T;
pre = Thrt;
InThreading(T);
pre->rchild = Thrt;
pre->RTag = 1;
Thrt->rchild = pre;
}
}
//-----先序遍历输出二叉树-----
void ShowBiTree(BiThrTree T)
{
if (T)
{
cout << T->data;
ShowBiTree(T->lchild);
ShowBiTree(T->rchild);
}
}
int main()
{
BiThrTree T = new BiThrNode;
const char* ch = "A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))";
//先序遍历构造二叉树
CreatBiTNode(T, ch);
//二叉树中序线索化
BiThrTree Thrt = new BiThrNode;
InOrderThreading(Thrt, T);
//输出根节点的前驱和后继
/*中序线索化时引入了头结点,
所以根节点的前驱即为头结点*/
cout << "根结点的前驱:" << Thrt << "(Thrt)"<<endl;
cout << "根结点的后继:" << Thrt->lchild->rchild->data;
}
4、构造哈夫曼树和哈夫曼编码的算法实现(返回目录)
#include"stdio.h"
#include"string.h"
#include"malloc.h"
#include"iostream"
using namespace std;
typedef struct HTNode {
unsigned int weight;
unsigned int parent, lchild, rchild;
}HTNode, * HuffTree;
typedef char** HuffCode;
//------选择权值最小的两个结点的下标-----
void Select(HuffTree& Ht, int m, int& S1, int& S2)
{
S1 = 0;
S2 = 0;
for (int j = 1; j <= m; j++)
{
if (Ht[j].parent == 0 && Ht[j].weight != 0)
{
if (Ht[j].weight < Ht[S1].weight)
S1 = j;
}
}
for (int j = 1; j <= m; j++)
{
if (Ht[j].parent == 0 && j != S1 && Ht[j].weight != 0)
{
if (Ht[j].weight < Ht[S2].weight)
S2 = j;
}
}
cout <<S1 << " " << S2 << endl;;
}
//-----哈夫曼编码函数-----
void HuffmanCoding(HuffTree& Ht, HuffCode& Hc, int n, char text[]) {
if (n <= 1)return;
int m, i, s1, s2;
HuffTree p;
m = 2 * n - 1;
Ht = (HuffTree)malloc((m + 1) * sizeof(HTNode));
for (p = Ht + 1, i = 1; i <= m; ++i, ++p) { //初始化哈夫曼树各个值
p->weight = 0;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
//读入一段文本
for (int i = 0; text[i] != '\0'; i++) {
Ht[text[i] - 'a' + 1].weight++; //当文本还没有结束时,统计各个字符的权值
}
//构建哈夫曼树
for (i = n + 1; i <= m; ++i) {
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;
}
// -------从叶子到根逆向求每个字符的哈夫曼编码------
Hc = (HuffCode)malloc((n + 1) * sizeof(char*)); //貌似不能用new,所以使用c语言的动态申请
char* cd = (char*)malloc((n) * sizeof(char));
cd[n - 1] = '\0'; //编码结束符
int start;
for (i = 1; i <= n; ++i) {
start = n - 1;
int parent = 0; //暂存父节点下标
int c = 0;
for (c = i, parent = Ht[i].parent; parent != 0; c = parent, parent = Ht[parent].parent)
{
if (Ht[parent].lchild == c)cd[--start] = '0';
else cd[--start] = '1';
}
//cd[n-1]='\0';
Hc[i] = new char[26];//动态申请某字母哈夫曼编码的空间
strcpy(Hc[i], &cd[start]);
}
free(cd);
}
//------输出哈夫曼编码------
void show(HuffCode& Hc, int n)
{
cout << "\n输出哈夫曼编码:\n";
for (int i = 1; i <= n; i++)
{
cout <<char(i-1+'a')<<":"<< Hc[i];
cout << "\n";
}
}
int main()
{
HuffTree ht;
HuffCode hc;
int n;
char text[] = "The Chinese official said he viewed the TrumpPresidency 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, haven’t 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";
char* text_transform = strlwr(text); //全部转化为小写
HuffmanCoding(ht, hc, 26, text_transform);
show(hc, 26);
}
四、实验过程原始数据记录
1、二叉树的基本操作算法实现
2、二叉树的各种遍历算法实现
3、线索二叉树的遍历
4、构造哈夫曼树和哈夫曼编码的算法实现
五、实验结果及分析
在二叉树的操作当中,有许多步骤的思路是一样的,所以递归在二叉树中的使用会更加广泛。我觉得递归的思想能在二叉树的操作中得到很好的锻炼。二叉树在数据压缩等方面以其的特性而广泛被应用,所以我觉得掌握二叉树在某些实际应用中的实现方法也是特别重要的。