一、实验目的
1、 熟练理解树和二叉树的相关概念,掌握的存储结构和相关操作实现;
2、 掌握树的顺序结构的实现;
3、 学会运用树的知识解决实际问题
二、 实验内容
1、自己确定一个二叉树(树结点类型、数目和结构自定)利用链式存储结构方法存储。实现树的构造,并完成:
1)用前序遍历、中序遍历、后序遍历输出结点数据;
2)以合理的格式,输出各个结点和双亲、孩子结点信息;
3)输出所有的叶子结点信息;
2、试设计一个程序,将输入的字符串转化为对应的哈夫曼编码,然后再将这个哈夫曼编码序列进行解码,也就是恢复原来的字符串序列。
三、编程实现
(一)链式存储二叉树
#include<iostream>
#include<string>
#include<iomanip>
using namespace std;
struct Node{
string data;
Node *lchild,*rchild;
};
class BinaryLinkedList{
private:
Node *root;
Node* create();
void _delete(Node *bt);
void preOrder(Node *bt);
void inOrder(Node *bt);
void postOrder(Node *bt);
void printInfo(Node *parent,Node *bt);
void leaf(Node *bt);
public:
BinaryLinkedList(){root=create();}
~BinaryLinkedList(){_delete(root);}
void preOrder(){
if(isEqual()){
cout<<"空二叉树!\n";
return;
}
preOrder(root);
}
void inOrder(){
if(isEqual()){
cout<<"空二叉树!\n";
return;
}
inOrder(root);
}
void postOrder(){
if(isEqual()){
cout<<"空二叉树!\n";
return;
}
postOrder(root);
}
bool isEqual(){
if(root==NULL)
return true;
else
return false;
}
void printInfo();
void Leaves(){
if(isEqual()){
cout<<"空二叉树!\n";
return;
}
leaf(root);
}
};
Node* BinaryLinkedList::create(){
Node *bt;
string s;
cin>>s;
if(s=="#") bt=NULL;
else{
bt=new Node;
bt->data=s;
bt->lchild=create();
bt->rchild=create();
}
return bt;
}
void BinaryLinkedList::_delete(Node *bt){
if(bt!=NULL){
_delete(bt->lchild);
_delete(bt->rchild);
delete bt;
}
}
void BinaryLinkedList::preOrder(Node *bt){
if(bt!=NULL){
cout<<bt->data<<" ";
preOrder(bt->lchild);
preOrder(bt->rchild);
}
}
void BinaryLinkedList::inOrder(Node *bt){
if(bt!=NULL){
inOrder(bt->lchild);
cout<<bt->data<<" ";
inOrder(bt->rchild);
}
}
void BinaryLinkedList::postOrder(Node *bt){
if(bt!=NULL){
postOrder(bt->lchild);
postOrder(bt->rchild);
cout<<bt->data<<" ";
}
}
void BinaryLinkedList::printInfo(){
if(isEqual()){
cout<<"空二叉树!\n";
return;
}
printInfo(NULL,root);
}
void BinaryLinkedList::printInfo(Node *parent,Node *bt){
cout<<setw(3)<<bt->data;
if(parent!=NULL) cout<<setw(8)<<parent->data;
else cout<<setw(8)<<"根";
if(bt->lchild!=NULL) cout<<setw(9)<<bt->lchild->data;
else cout<<setw(9)<<"无";
if(bt->rchild!=NULL) cout<<setw(11)<<bt->rchild->data<<endl;
else cout<<setw(12)<<"无\n";
if(bt->lchild!=NULL) printInfo(bt,bt->lchild);
if(bt->rchild!=NULL) printInfo(bt,bt->rchild);
}
void BinaryLinkedList::leaf(Node *bt){
if(bt->lchild==NULL && bt->rchild==NULL){
cout<<bt->data<<" ";
}else{
if(bt->lchild!=NULL) leaf(bt->lchild);
if(bt->rchild!=NULL) leaf(bt->rchild);
}
}
int main(){
cout<<"创建二叉树,结点信息为空输入#\n";
BinaryLinkedList b;
cout<<"前序遍历为:";
b.preOrder();
cout<<endl;
cout<<"中序遍历为:";
b.inOrder();
cout<<endl;
cout<<"后序遍历为:";
b.postOrder();
cout<<endl;
cout<<"\n 信息表\n";
cout<<"---------------------------------\n";
cout<<"结点 双亲 左孩子 右孩子\n\n";
b.printInfo();
cout<<"---------------------------------\n";
cout<<"\n所有叶子结点为:";
b.Leaves();
cout<<endl;
return 0;
}
运行结果:
(二)哈夫曼编码
1.使用链表存放输入字符串中出现的字符及其权值(出现次数)
①LinkedList.h
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
struct LeafNode{
char value;
int weight;
LeafNode *next;
};
class LinkedList{
private:
LeafNode *first,*rear;
int length;
public:
LinkedList();
~LinkedList();
int getLength();
LeafNode* getFirst();
void add(char v,int w);
};
#endif
#include"LinkedList.h"
#include<cstddef>
LinkedList::LinkedList(){
first=new LeafNode;
first->next=NULL;
rear=first;
length=0;
}
LinkedList::~LinkedList(){
LeafNode *p=first;
while(p!=NULL){
first=first->next;
delete p;
p=first;
}
}
int LinkedList::getLength(){
return length;
}
LeafNode* LinkedList::getFirst(){
return first;
}
void LinkedList::add(char v,int w){
LeafNode *s=new LeafNode;
s->value=v;
s->weight=w;
s->next=rear->next;
rear->next=s;
rear=s;
length++;
}
①LinkedStack.h
#ifndef LINKEDSTACK_H
#define LINKEDSTACK_H
struct StackNode{
char data;
StackNode *next;
};
class LinkedStack{
private:
StackNode *top;
public:
LinkedStack();
~LinkedStack();
void push(char c);
char pop();
StackNode* getTop();
};
#endif
#include"LinkedStack.h"
#include<cstddef>
LinkedStack::LinkedStack(){
top=NULL;
}
LinkedStack::~LinkedStack(){
StackNode *p;
while(top!=NULL){
p=top;
top=top->next;
delete p;
}
}
void LinkedStack::push(char c){
StackNode *s=new StackNode;
s->data=c;
s->next=top;
top=s;
}
char LinkedStack::pop(){
StackNode *p=top;
top=top->next;
char c=p->data;
delete p;
return c;
}
StackNode* LinkedStack::getTop(){
return top;
}
①LinkQueue.h
#ifndef LINKQUEUE_H
#define LINKQUEUE_H
struct QueueNode{
char data;
QueueNode *next;
};
class LinkQueue{
private:
QueueNode *front,*rear;
public:
LinkQueue();
~LinkQueue();
void enQueue(char c);
char deQueue();
QueueNode* getFront();
};
#endif
#include"LinkQueue.h"
#include<cstddef>
LinkQueue::LinkQueue(){
QueueNode *s=new QueueNode;
s->next=NULL;
front=rear=s;
}
LinkQueue::~LinkQueue(){
QueueNode *p;
while(front!=NULL){
p=front;
front=front->next;
delete p;
}
}
void LinkQueue::enQueue(char c){
QueueNode *s=new QueueNode;
s->data=c;
s->next=NULL;
rear->next=s;
rear=s;
}
char LinkQueue::deQueue(){
QueueNode *p=front->next;
char c=p->data;
front->next=p->next;
if(front->next==NULL) rear=front;
delete p;
return c;
}
QueueNode* LinkQueue::getFront(){
return front;
}
①HuffmanTree.h
#ifndef HUFFMANTREE_H
#define HUFFMANTREE_H
#include<iostream>
#include<string>
#include"LinkedList.h"
#include"LinkedStack.h"
#include"LinkQueue.h"
using namespace std;
struct Node{
char value;
int weight;
int lchild,rchild,parent;
};
class HuffmanTree{
private:
int length; //叶子结点个数
Node *p=new Node[2*length-1]; //保存哈夫曼树各结点信息
int* select(int k); //寻找权值最小的两个结点下标,用数组a[2]存放
public:
HuffmanTree(int len,LinkedList l);
~HuffmanTree();
void code(string s); //编码
void decode(); //解码
};
#endif
#include"HuffmanTree.h"
HuffmanTree::HuffmanTree(int len,LinkedList l){
length=len;
for(Node *q=p;q!=p+(2*length-1);q++){ //先把存放哈夫曼树的数组初始化
q->lchild=-1;
q->rchild=-1;
q->parent=-1;
}
LeafNode *pointer=l.getFirst()->next;
for(Node *q=p;q!=p+length;q++){ //把得到的链表信息存放到数组中
if(pointer!=NULL){
q->value=pointer->value;
q->weight=pointer->weight;
pointer=pointer->next;
}
}
l.~LinkedList(); //销毁链表
for(int k=length;k<2*length-1;k++){ //构造哈夫曼树
int *a;
a=select(k); //返回存放权值最小的两个根结点下标的数组a
Node *q=p+k; //合并生成的根结点信息存放在数组对应下标为k的位置,对应元素q
(p+a[0])->parent=k;
(p+a[1])->parent=k; //两个权值最小的结点双亲设置为k
q->weight=(p+a[0])->weight+(p+a[1])->weight; //两个最小结点的权值之和为生成结点q的权值
q->lchild=a[0];
q->rchild=a[1]; //把两个最小结点设置为q的左右孩子
delete[] a; //释放动态申请的数组a
}
}
HuffmanTree::~HuffmanTree(){
delete[] p;
}
int* HuffmanTree::select(int k){ //查找权值最小的两个结点对应下标,查找范围在下标为0~k-1内
Node *pointer; //记录查找过程目前对应权值最小的结点
Node *q=p;
int count=0,*a=new int[2]; //a[0]记录权值最小结点的下标,a[1]记录权值第二小的结点下标,count记录查找时位置即下标
for(int i=0;i<k;i++){ //先找出第一个无双亲的结点下标赋给a[0]
if((p+i)->parent==-1){
a[0]=i;
break;
}
}
pointer=p+a[0]; //先默认a[0]对应权值最小,其后若找到权值更小的则更新a[0]为权值更小的对应下标
while(q!=p+k){ //先找出权值最小的结点下标赋给a[0],查找的结点都是无双亲
if(q->parent==-1){
if((pointer->weight)>(q->weight)){
pointer=q;
a[0]=count;
}
}
q++;
count++;
}
for(int i=0;i<k;i++){ //先找出第一个无双亲且其下标不是a[0]的结点对应下标赋给a[1]
if((p+i)->parent==-1 && i!=a[0]){
a[1]=i;
break;
}
}
pointer=p+a[1]; //查找权值第二小的结点下标,跟a[0]相似,只是多了不跟a[0]相同的条件,使得a[0]与a[1]不重复
q=p;
count=0;
while(q!=p+k){
if(q->parent==-1 && count!=a[0]){
if((pointer->weight)>(q->weight)){
pointer=q;
a[1]=count;
}
}
q++;
count++;
}
return a;
}
void HuffmanTree::code(string s){ //编码
LinkedStack l;
for(int i=0;i<s.size();i++){
for(int j=0;j<length;j++){ //查找第i个字符在哈夫曼树的叶子结点的位置
if(s[i]==(p+j)->value){ //找到在下标为j的位置,将该字符的哈夫曼编码入栈再弹栈
while((p+j)->parent!=-1){
Node *par=p+(p+j)->parent;
if(j==(par->lchild)){
l.push('0');
}else if(j==(par->rchild)){
l.push('1');
}
j=(p+j)->parent;
}
while(l.getTop()!=NULL){
cout<<l.pop();
}
break; //找到该字符后进行下一个字符的编码
}else continue;
}
}
cout<<endl;
}
void HuffmanTree::decode(){
LinkQueue queue;
string str;
cout<<"请输入编码串:";
getline(cin,str);
int parent=2*length-2; //parent表示解码时到达的结点位置即下标
for(int i=0;i<str.size();i++){ //根据编码顺着路径到达叶子结点后,将对应字符入队,最后依次出队
if(str[i]=='0'){
parent=(p+parent)->lchild;
if((p+parent)->lchild==-1 && (p+parent)->rchild==-1){ //若已经到了叶子结点,将字符入队
queue.enQueue((p+parent)->value);
parent=2*length-2;
}
}else if(str[i]=='1'){
parent=(p+parent)->rchild;
if((p+parent)->lchild==-1 && (p+parent)->rchild==-1){
queue.enQueue((p+parent)->value);
parent=2*length-2;
}
}else{ //若编码存在'0'和'1'之外的字符则编码有误
cout<<"\n该编码有误!\n";
break;
}
}
cout<<"解码为:";
while(queue.getFront()->next!=NULL){
cout<<queue.deQueue();
}
cout<<endl;
}
5.main.cpp
#include<iostream>
#include<string>
#include"LinkedList.h"
#include"HuffmanTree.h"
using namespace std;
int main(){
cout<<"请输入字符串:";
string s; //s存放输入的字符串
getline(cin,s);
if(s.size()==0) return 0;
LinkedList l;
for(int i=0;i<s.size();i++){
for(int j=0,count=1;j<=i;j++){
if(j==i){
for(int k=i+1;k<s.size();k++){
if(s[k]==s[i]){
count++;
}
}
l.add(s[i],count); //把字符及其权值(出现次数)存放在链表中
break;
}
if(s[i]==s[j]) break;
}
}
HuffmanTree h(l.getLength(),l); //创建哈夫曼树
cout<<"编码为:";
h.code(s);
cout<<endl;
h.decode();
return 0;
}
运行结果: