实验六 树和二叉树的实验2

一、实验目的

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


②LinkedList.cpp

#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++;
}


2.编码时使用链栈保存从叶子结点到根结点的哈夫曼编码

①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


②LinkedStack.cpp

#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;
}


3.解码时使用队列保存顺着路径找到的字符

①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


②LinkQueue.cpp

#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;
}


4.构造哈夫曼树并实现编码与解码

①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


②HuffmanTree.cpp

#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;
}

运行结果:



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值