data
和双亲域parent
。只记录了每个节点的双亲,无法直接得到该节点的孩子。lchild
和rchild
,称之为二叉链表。lchild
存储第1个孩子的地址,rchild
存储其右兄弟的地址。(将长子当做左孩子,将兄弟关系向右斜)k
的二叉树至多有2^{k}-1
个节点。一颗完全二叉树有 1001 1001 1001个节点,其中叶子节点的个数是多少?
答: 首先找到最后一个结点 1001 1001 1001的双亲节点,其双亲节点编号为 1001 / 2 = 500 1001/2 = 500 1001/2=500,该节点是最后一个拥有孩子的节点,其后面全是叶子,即 1001 − 500 = 501 1001-500 = 501 1001−500=501个叶子。
一颗完全二叉树的第 6 6 6层有 8 8 8个叶子,则该完全二叉树最少有多少个节点,最多有多少个节点?
答: 节点最少的情况( 6 6 6层): 8 8 8个叶子在最后一层(第 6 6 6层),前 5 5 5层是满的,最少有 2 5 − 1 + 8 = 39 2^{5} - 1 + 8 = 39 25−1+8=39个节点;节点最多的情况( 7 7 7层):8个叶子在倒数第 2 2 2层(即第 6 6 6层),前 6 6 6层是满的,第 7 7 7层最少缺失 8 × 2 8 \times 2 8×2个节点,因为第 6 6 6层的 8 8 8个叶子如果生成孩子的话,会有 16 16 16个节点。最多有 2 7 − 1 − 16 = 111 2^{7}-1-16 = 111 27−1−16=111个节点。
typedef struct Bnode{
int data;
struct Bnode *lchild,*rchild; //左孩子指针,右孩子指针
}Bnode,*Btree; //类型名Bnode,指针Btree
typedef struct Bnode{
int data;
struct Bnode *lchild,*rchild,*parent; //左孩子指针,右孩子指针,双亲指针
}Bnode,*Btree; //类型名Bnode,指针Btree
#include
using namespace std;
typedef struct Bnode{
char data;
struct Bnode *lchild,*rchild; //左孩子指针,右孩子指针
}Bnode,*Btree; //类型名Bnode,指针Btree
void createtree(Btree &T); //创建二叉树函数(询问法)
int main(){
Btree cs;
createtree(cs);
}
void createtree(Btree &T){
char check; //判断是否创建左右孩子
T = new Bnode;
cout << "请输入节点数据:" << endl;
cin >> T->data;
cout << "是否填加"<< T->data <<"的左孩子? (Y/N)"<<endl; //询问创建T的左子树
cin >> check;
if(check == 'Y'){
createtree(T->lchild);
}
else{
T->lchild = NULL;
}
cout << "是否填加 "<< T->data <<"的右孩子? (Y/N)"<<endl; //询问创建T的右子树
cin >> check;
if(check == 'Y'){
createtree(T->rchild);
}
else{
T->rchild = NULL;
}
}
输入:
请输入节点数据:
A
是否填加A的左孩子? (Y/N)
Y
请输入节点数据:
B
是否填加B的左孩子? (Y/N)
Y
请输入节点数据:
D
是否填加D的左孩子? (Y/N)
N
是否填加D的右孩子? (Y/N)
N
是否填加B的右孩子? (Y/N)
Y
请输入节点数据:
E
是否填加E的左孩子? (Y/N)
N
是否填加E的右孩子? (Y/N)
N
是否填加A的右孩子? (Y/N)
Y
请输入节点数据:
C
是否填加C的左孩子? (Y/N)
Y
请输入节点数据:
F
是否填加F的左孩子? (Y/N)
N
是否填加F的右孩子? (Y/N)
Y
请输入节点数据:
G
是否填加G的左孩子? (Y/N)
N
是否填加G的右孩子? (Y/N)
N
是否填加C的右孩子? (Y/N)
N
#include
using namespace std;
typedef struct Bnode{
char data;
struct Bnode *lchild,*rchild; //左孩子指针,右孩子指针
}Bnode,*Btree; //类型名Bnode,指针Btree
void createtree_bk(Btree &T); //创建二叉树函数(补空法)
int main(){
Btree cs;
createtree_bk(cs);
}
void createtree_bk(Btree &T){
char ch;
cin >> ch; //二叉树补空后,按先序遍历序列输入字符
if(ch == '#'){
T = NULL;
}
else{
T = new Bnode;
T->data = ch; //生产根节点
createtree_bk(T->lchild); //递归创建左子树
createtree_bk(T->rchild); //递归创建右子树
}
}
输入:
ABD##E##CF#G###
#include
using namespace std;
typedef struct Bnode{
char data;
struct Bnode *lchild,*rchild; //左孩子指针,右孩子指针
}Bnode,*Btree; //类型名Bnode,指针Btree
void createtree_bk(Btree &T); //创建二叉树函数(补空法)
void preorder(Btree T); //先序遍历
int main(){
Btree cs;
createtree_bk(cs);
preorder(cs);
}
void createtree_bk(Btree &T){
char ch;
cin >> ch; //二叉树补空后,按先序遍历序列输入字符
if(ch == '#'){
T = NULL;
}
else{
T = new Bnode;
T->data = ch; //生产根节点
createtree_bk(T->lchild); //递归创建左子树
createtree_bk(T->rchild); //递归创建右子树
}
}
void preorder(Btree T){
if(T){
cout << T->data << " ";
preorder(T->lchild);
preorder(T->rchild);
}
}
#include
using namespace std;
typedef struct Bnode{
char data;
struct Bnode *lchild,*rchild; //左孩子指针,右孩子指针
}Bnode,*Btree; //类型名Bnode,指针Btree
void createtree_bk(Btree &T); //创建二叉树函数(补空法)
void preorder(Btree T); //先序遍历
void inorder(Btree T); //中序遍历
int main(){
Btree cs;
createtree_bk(cs);
preorder(cs);
cout << endl;
inorder(cs);
}
void createtree_bk(Btree &T){
char ch;
cin >> ch; //二叉树补空后,按先序遍历序列输入字符
if(ch == '#'){
T = NULL;
}
else{
T = new Bnode;
T->data = ch; //生产根节点
createtree_bk(T->lchild); //递归创建左子树
createtree_bk(T->rchild); //递归创建右子树
}
}
void preorder(Btree T){
if(T){
cout << T->data << " ";
preorder(T->lchild);
preorder(T->rchild);
}
}
void inorder(Btree T){
if(T){
inorder(T->lchild);
cout << T->data << " ";
inorder(T->rchild);
}
}
输入:
ABD##E##CF#G###
输出:
A B D E C F G
D B E A F G C
#include
using namespace std;
typedef struct Bnode{
char data;
struct Bnode *lchild,*rchild; //左孩子指针,右孩子指针
}Bnode,*Btree; //类型名Bnode,指针Btree
void createtree_bk(Btree &T); //创建二叉树函数(补空法)
void preorder(Btree T); //先序遍历
void inorder(Btree T); //中序遍历
void posorder(Btree T); //后序遍历
int main(){
Btree cs;
createtree_bk(cs);
preorder(cs);
cout << endl;
inorder(cs);
cout <<endl;
posorder(cs);
}
void createtree_bk(Btree &T){
char ch;
cin >> ch; //二叉树补空后,按先序遍历序列输入字符
if(ch == '#'){
T = NULL;
}
else{
T = new Bnode;
T->data = ch; //生产根节点
createtree_bk(T->lchild); //递归创建左子树
createtree_bk(T->rchild); //递归创建右子树
}
}
void preorder(Btree T){
if(T){
cout << T->data << " ";
preorder(T->lchild);
preorder(T->rchild);
}
}
void inorder(Btree T){
if(T){
inorder(T->lchild);
cout << T->data << " ";
inorder(T->rchild);
}
}
void posorder(Btree T){
if(T){
posorder(T->lchild);
posorder(T->rchild);
cout << T->data << " ";
}
}
输入:
ABD##E##CF#G###
输出:
A B D E C F G
D B E A F G C
D E B G F C A
#include
#include
using namespace std;
typedef struct Bnode{
char data;
struct Bnode *lchild,*rchild; //左孩子指针,右孩子指针
}Bnode,*Btree; //类型名Bnode,指针Btree
void createtree_bk(Btree &T); //创建二叉树函数(补空法)
bool Leveltraverse(Btree T); //层次遍历
int main(){
Btree cs;
createtree_bk(cs);
Leveltraverse(cs);
}
void createtree_bk(Btree &T){
char ch;
cin >> ch; //二叉树补空后,按先序遍历序列输入字符
if(ch == '#'){
T = NULL;
}
else{
T = new Bnode;
T->data = ch; //生成根节点
createtree_bk(T->lchild); //递归创建左子树
createtree_bk(T->rchild); //递归创建右子树
}
}
bool Leveltraverse(Btree T){
Btree p;
if(!T){
return false;
}
queue<Btree>Q; //创建一个普通队列(先进先出),里面存放指针类型
Q.push(T); //根指针入队
while(!Q.empty()) //如果队列不空
{
p=Q.front();//取出队头元素作为当前扩展结点livenode
Q.pop(); //队头元素出队
cout<<p->data<<" ";
if(p->lchild){
Q.push(p->lchild); //左孩子指针入队
}
if(p->rchild){
Q.push(p->rchild); //右孩子指针入队
}
}
return true;
}
输入:
ABD##E##CF#G###
输出:
A B C D E F G
#include
#include
using namespace std;
typedef struct Bnode{
char data;
struct Bnode *lchild,*rchild; //左孩子指针,右孩子指针
}BiTNode,*BiTree; //类型名Bnode,指针Btree
BiTree pre_mid_createBiTree(char *pre,char *mid,int len); //由先序、中序还原建立二叉树
void posorder(BiTree T); //遍历二叉树,以后序序列输出
int main(){
char xxxl[] = {'A','B','D','E','C','F','G'}; //先序序列
char zxxl[] = {'D','B','E','A','F','G','C'}; //中序序列
int len = strlen(xxxl); //字符数组长度
BiTree cs = pre_mid_createBiTree(xxxl,zxxl,len);
posorder(cs); //以后序序列输出
}
BiTree pre_mid_createBiTree(char *pre,char *mid,int len){
if(len == 0){
return NULL;
}
char ch = pre[0]; //先序序列中的第1个节点,作为根
int index = 0; //在中序序列中查找根结点,并用index记录查找长度
while(mid[index] != ch){ //在中序序列中查找根结点,左边为该节点的左子树
index++;
}
BiTree T = new BiTNode ; //创建根节点
T->data = ch;
T->lchild = pre_mid_createBiTree(pre+1,mid,index); //创建左子树
T->rchild = pre_mid_createBiTree(pre+index+1, mid+index+1,len-index-1); //创建右子树
return T;
}
void posorder(BiTree T){
if(T){
posorder(T->lchild);
posorder(T->rchild);
cout << T->data << " ";
}
}
输出:
D E B G F C A
#include
#include
using namespace std;
typedef struct Bnode{
char data;
struct Bnode *lchild,*rchild; //左孩子指针,右孩子指针
}BiTNode,*BiTree; //类型名Bnode,指针Btree
BiTree pre_mid_createBiTree(char *last,char *mid,int len); //由先序、中序还原建立二叉树
void preorder(BiTree T); //遍历二叉树,以后前序列输出
int main(){
char hxxl[] = {'D','E','B','G','F','C','A'}; //后序序列
char zxxl[] = {'D','B','E','A','F','G','C'}; //中序序列
int len = strlen(hxxl);
BiTree cs = pre_mid_createBiTree(hxxl,zxxl,len);
preorder(cs); //以先序序列输出
}
BiTree pre_mid_createBiTree(char *last,char *mid,int len){
if(len == 0){
return NULL;
}
char ch = last[len-1]; //找到后序序列中的最后一个节点,作为根
int index = 0; //在中序序列中查找根结点,并用index记录查找长度
while(mid[index] != ch){ //在中序序列中查找根结点,左边为该节点的左子树,右边为右子树
index++;
}
BiTree T = new BiTNode ; //创建根节点
T->data = ch;
T->lchild = pre_mid_createBiTree(last,mid,index); //创建左子树
T->rchild = pre_mid_createBiTree(last+index, mid+index+1,len-index-1); //创建右子树
return T;
}
void preorder(BiTree T){
if(T){
cout << T->data << " ";
preorder(T->lchild);
preorder(T->rchild);
}
}
输出:
A B D E C F G
输入一颗二叉树,输出其先序遍历序列。
输入:第1行为二叉树的节点数 n ( 1 ≤ n ≤ 26 ) n(1 \leq n \leq 26) n(1≤n≤26)。后面的 n n n行,以每一个字母为节点,后两个字母分别为其左、右孩子。对空节点用*
表示。
输出:输出二叉树的先序遍历序列。
#include
using namespace std;
string s;
int n,root,l[100],r[100];
void preorder(int t); //先序遍历
int main(){
cin >> n;
for (int i = 0; i < n; ++i) {
cin >> s;
if(!i) {
root = s[0] - 'a';
}
l[s[0]-'a'] = s[1] - 'a';
r[s[0]-'a'] = s[2] - 'a';
}
preorder(root);
return 0;
}
void preorder(int t){
if(t != '*'-'a'){
cout << char(t + 'a');
preorder(l[t]);
preorder(r[t]);
}
}
输入:
6
abc
bdi
cj*
d**
i**
j**
输出:
abdicj
小瓦伦丁非常喜欢玩二叉树。她最喜欢的游戏是根据二叉树节点的大写字母随机构造二叉树。
为了记录她的树,她为每棵树都写下两个字符串:一个先序遍历(根、左子树、右子树)和一个中序遍历(左子树、根、右子树)。
输入:输入包含一个或多个测试用例。每个测试用例都包含一行,其中包含两个字符串,表示二叉树的先序遍历和中序遍历。两个两个字符串都由唯一的大写字母组成。
输出:对于每个测试用例,都单行输出该二叉树的后序遍历序列(左子树、右子树、根)
#include
#include
using namespace std;
string preorder,inorder;
void postorder(int l1, int l2, int n);
int main(){
while(cin >> preorder >> inorder){
int len = preorder.size();
postorder(0,0,len);
cout << endl;
}
return 0;
};
void postorder(int l1, int l2, int n){
if(n <= 0){
return;
}
int len = inorder.find(preorder[l1])-l2;
postorder(l1+1,l2,len);
postorder(l1+len+1,l2+len+1,n-len-1);
cout << preorder[l1];
}
输入:
DBACEGF ABCDEFG
BCAD CBAD
输出:
ACBFGED
CDAB
确定给定二叉树中的一个叶子节点,使从根到叶子路径上的节点权值之和最小。
节点权值:二叉树中的权值就是对叶子结点赋予的一个有意义的数量值。
输入:输入包含二叉树的中序遍历和后序遍历。从输入文件中读取两行(直到文件结束)。第1行包含与中序遍历相关联的值序列,第2行包含与后序遍历相关联的值的序列。所有值均不同,都大于零且小于10000。假设没有二叉树超过10000个节点或少于1个节点。
输出:对于每棵二叉树,都输出值最小的路径上叶子节点的值。如果多条路径的值最小,则选择叶子节点值最小的路径。
#include
#include
#include
using namespace std;
const int maxn=10000+5;
int inorder[maxn],postorder[maxn],lch[maxn],rch[maxn];
int n,minv,minsum;
int createtree(int l1,int l2,int m); //由遍历序列创建二叉树
bool readline(int *a); //读入遍历序列,中间有空格
void findmin(int v,int sum);
int main(){
while(readline(inorder)){ //读入中序序列
readline(postorder); //读入后序序列
createtree(0,0,n);
minsum = 0x7fffffff;
findmin(postorder[n-1],0);
cout << minv << endl;
}
return 0;
};
int createtree(int l1,int l2,int m){
if(m<=0){
return 0;
}
int root = postorder[l2+m-1];
int len = 0;
while(inorder[l1+len]!=root){ //计算左子树的长度
len++;
}
lch[root] = createtree(l1,l2,len);
rch[root] = createtree(l1+len+1,l2+len,m-len-1);
return root;
}
bool readline(int *a){
string line;
if(!getline(cin,line)){
return false;
}
stringstream s(line);//可以用于分割被空格、制表符等符号分割的字符串
n = 0;
int x;
while(s >> x){
a[n++] = x;
}
return n>0;
}
void findmin(int v,int sum){
sum+=v;
if(!lch[v]&&!rch[v]){//叶子
if(sum < minsum || (sum == minsum && v<minv)){
minv = v;
minsum = sum;
}
}
if(lch[v]){ //v有左子树
findmin(lch[v],sum);
}
if(rch[v]){ //v有右子树
findmin(rch[v],sum);
}
}
输入:
3 2 1 4 5 7 6
3 1 2 5 6 7 4
7 8 11 3 5 16 12 18
8 3 11 7 16 18 12 5
255
255
输出:
1
3
255
初始化。构造 n n n棵节点为 n n n个字符的单节点树集合 T = { t 1 , t 2 , t 3 , . . . , t n } T=\{t_{1},t_{2},t_{3},...,t_{n}\} T={t1,t2,t3,...,tn},每棵树只有一个带权的根节点权值为该字符的使用评率。
如果在 T T T中只剩下一颗树,则哈夫曼树构造成功,跳到第6步。否则,从集合T中取出没有双亲且权值最小的两颗树 t i t_{i} ti和 t j t_{j} tj,将它们合并成一颗新树 z k z_{k} zk,新树的左孩子为 t i t_{i} ti,右孩子为 t j t_{j} tj, z k z_{k} zk的权值为 t i t_{i} ti和 t j t_{j} tj的权值之和。
从集合 T T T中删去 t i t_{i} ti、 t j t_{j} tj,加入 z k z_{k} zk。
重复第3~4步。
约定左分支上的编码为“0”,右分支上的编码为“1”。从叶子节点到根节点逆向求出每个字符的哈夫曼编码。那么从根节点到叶子节点路径上的字符组成的字符串为该叶子节点的哈夫曼编码,算法结束。
typedef struct {
double weight; //权值
int parent; //双亲
int lchild; //左孩子
int rchild; //右孩子
char value; //该节点表示的字符
} HNodeType;
bit[]
存放结点的编码,start
记录编码开始时的下标,在逆向编码存储时,start
从 n − 1 n-1 n−1开始依次递减,从后向前存储;当读取时,从start+1
开始到 n − 1 n-1 n−1,从前向后输出,即该字符的编码。编码结构体如下:typedef struct{
int bit[MAXBIT]; //存储编码的数组
int start; //编码开始下标
} HCodeType;
初始化。初始化哈夫曼树数组HuffNode[]中的节点权值为0,双亲和左、右孩子均为-1,然后读入叶子节点的权值。
循环构造哈夫曼树。从集合 T T T中取出双亲为-1且权值最小的两棵树 t i t_{i} ti和 t j t_{j} tj,将它们合并成一棵新树 z k z_{k} zk,新树的左孩子为 t i t_{i} ti,右孩子为 t j t_{j} tj, z k z_{k} zk的权值为 t i t_{i} ti和 t j t_{j} tj的权值之和。
输出哈夫曼编码。
#include
#include
#include
using namespace std;
#define MAXBIT 100
#define MAXVALUE 10000
#define MAXLEAF 30
#define MAXNODE MAXLEAF*2 -1
typedef struct
{
double weight;
int parent;
int lchild;
int rchild;
char value;
} HNodeType; /* 节点结构体 */
typedef struct
{
int bit[MAXBIT];
int start;
} HCodeType; /* 编码结构体 */
HNodeType HuffNode[MAXNODE]; /* 定义一个节点结构体数组 */
HCodeType HuffCode[MAXLEAF];/* 定义一个编码结构体数组*/
void HuffmanTree (HNodeType HuffNode[MAXNODE], int n);/* 构造哈夫曼树 */
void HuffmanCode(HCodeType HuffCode[MAXLEAF], int n);/* 哈夫曼树编码 */
int main()
{
int i,j,n;
cout<<"Please input n:"<<endl;
cin>>n;
HuffmanTree(HuffNode,n); //构造哈夫曼树
HuffmanCode(HuffCode,n); // 哈夫曼树编码
//输出已保存好的所有存在编码的哈夫曼编码
for(i=0;i<n;i++)
{
cout<<HuffNode[i].value<<": Huffman code is: ";
for(j=HuffCode[i].start+1;j<n;j++)
cout<<HuffCode[i].bit[j];
cout<<endl;
}
return 0;
}
void HuffmanTree (HNodeType HuffNode[MAXNODE], int n){
int i, j; //循环变量
int x1, x2; //构造哈夫曼树不同过程中两个最小权值节点的权值
double m1,m2; //构造哈夫曼树不同过程中两个最小权值节点在数组中的序号
/* 初始化存放哈夫曼树数组 HuffNode[] 中的节点 */
for (i=0; i<2*n-1;i++)
{
HuffNode[i].weight=0;//权值
HuffNode[i].parent=-1;
HuffNode[i].lchild=-1;
HuffNode[i].rchild=-1;
}
/* 输入 n 个叶子节点的权值 */
for (i=0; i<n; i++)
{
cout<<"Please input value and weight of leaf node "<<i+1<<endl;
cin>>HuffNode[i].value>>HuffNode[i].weight;
}
/* 构造 Huffman 树 */
for (i=0; i<n-1; i++)
{//执行n-1次合并
m1=m2=MAXVALUE;
/* m1、m2中存放两个无父节点且节点权值最小的两个节点 */
x1=x2=0;
/* 找出所有节点中权值最小、无父节点的两个节点,并合并之为一棵二叉树 */
for (j=0;j<n+i;j++)
{
if (HuffNode[j].weight<m1&&HuffNode[j].parent==-1)
{
m2 = m1;
x2 = x1;
m1 = HuffNode[j].weight;
x1 = j;
}
else if (HuffNode[j].weight < m2 && HuffNode[j].parent==-1)
{
m2=HuffNode[j].weight;
x2=j;
}
}
/* 设置找到的两个子节点 x1、x2 的父节点信息 */
HuffNode[x1].parent = n+i;
HuffNode[x2].parent = n+i;
HuffNode[n+i].weight = m1+m2;
HuffNode[n+i].lchild = x1;
HuffNode[n+i].rchild = x2;
cout<<"x1.weight and x2.weight in round "<<i+1<<"\t"<<HuffNode[x1].weight<<"\t"<<HuffNode[x2].weight<<endl; /* 用于测试 */
}
}
void HuffmanCode(HCodeType HuffCode[MAXLEAF], int n){
HCodeType cd; /* 定义一个临时变量来存放求解编码时的信息 */
int i,j,c,p;
for(i=0;i<n;i++)
{
cd.start=n-1;
c=i;
p=HuffNode[c].parent;
while(p!=-1)
{
if(HuffNode[p].lchild==c)
cd.bit[cd.start]=0;
else
cd.bit[cd.start]=1;
cd.start--; /*前移一位 */
c=p;
p=HuffNode[c].parent; /* 设置下一循环条件 */
}
/* 把叶子节点的编码信息从临时编码cd中复制出来,放入编码结构体数组 */
for (j=cd.start+1; j<n; j++)
HuffCode[i].bit[j]=cd.bit[j];
HuffCode[i].start=cd.start;
}
}
约翰想修牧场周围的篱笆,需要 N N N块( 1 ≤ N ≤ 20000 1 \leq N \leq 20000 1≤N≤20000)木板,每块木板都具有整数长度 L i L_{i} Li( 1 ≤ L i ≤ 50000 1 \leq L_{i} \leq 50000 1≤Li≤50000)米。他购买了一块足够长的木板(长度为 L i L_{i} Li的总和, i = 1 , 2 , . . . , N i = 1,2,...,N i=1,2,...,N),以便得到 N N N块木板。切割时木屑损失的长度不计。
农夫唐向约翰收取切割费用。切割一块木板的费用与其长度相同。切割长度为21米的木板需要21美分。唐让约翰决定切割木板的顺序和位置。约翰知道以不同的顺序切割木板,将会产生不同的费用。帮助约翰确定他得到N块木板的最低金额。
输入:第1行包含一个整数N,表示木板的数量。第2~N+1行,每行都包含一个所需木板的长度L_{i}。
输出:一个整数,即进行N-1次切割的最低花费。
sum
为所需花费。#include
#include
int main(){
using namespace std;
long long sum;
int n,t,t1,t2;
while(cin >> n){
priority_queue<int,vector<int>,greater<int>>q; //从最小值开始弹出
for (int i = 0; i < n; ++i) {
cin >> t;
q.push(t);
}
sum = 0;
if(q.size() == 1){
t1 = q.top();
sum+=t1;
q.pop();
}
while(q.size()>1){
t1 = q.top(),q.pop();
t2 = q.top(),q.pop();
t = t1 + t2;
sum += t;
q.push(t);
}
cout << sum << endl;
}
return 0;
}
输入:
3
8
5
8
输出:
34
熵编码是一种数据编码方法,通过对去除“冗余”或“额外”信息的消息进行编码来实现无损数据压缩。为了能够恢复信息,编码字形的为位模式不允许作为任何其他编码位模式的前缀。
输入:输入文件跑酷哦一个字符串列表,每行一个。字符串将只包含大写字母、数字字符和下划线(用于替代空格)。以字符串“END”结尾,不应处理此行。
输出:对于每个字符串,都输出8位ASCII编码的位长度、最佳无前缀可变长度编码的位长度及精确到一个小数点的压缩比;
#include
#include
#include
#include
int main(){
using namespace std;
int a[27];
string s;
while(1){
cin >> s;
if(s == "END"){
break;
}
memset(a,0,sizeof(a)); //数组初始化
int n = s.size();
for (int i = 0; i < n; ++i) {
if(s[i] == '_'){
a[26]++;
}
else{
a[s[i]-'A']++;
}
}
priority_queue<int,vector<int>,greater<int> >q; //优先队列
for (int i = 0; i <= 26; ++i) {
if(a[i]){ //将数组中非零元素入队
q.push(a[i]);
}
}
int ans = n;
while(q.size()>2){
int t,t1,t2;
t1 = q.top(),q.pop();
t2 = q.top(),q.pop();
t = t1+t2;
ans += t;
q.push(t);
}
printf("%d %d %.1lf\n",n*8,ans,(double)n*8/ans);
}
return 0;
}
输入:
AAAAABCD
THE_CAT_IN_THE_HAT
END
输出:
64 13 4.9
144 51 2.8
静态哈夫曼编码是一种主要用于文本压缩的编码算法。给定一个由 N N N各不同字符组成的特定长度的文本,算法选择 N N N个编码,每个不同的字符都对应一个编码。使用这些编码压缩文本,当选择编码算法构建一个具有 N N N个叶子的二叉树时,对于 N ≤ 2 N \leq 2 N≤2,树的构建流程如下
对于文本中的每个不同字符,都构建一个仅包含单个节点的树,其权值为该字符在文本中出现的次数。
构建一个包含上述 N N N棵树的集合 S S S。
当 S S S包含多于一棵树时:①选择最小的权值 t 1 ∈ S t_{1} \in S t1∈S,并将其从 S S S中删除;②选择最小的权值 t 2 ∈ S t_{2} \in S t2∈S,并将其从 S S S中删除;③构建一棵新树 t t t, t 1 t_{1} t1为其左子树, t 2 t_{2} t2为其右子树,t的权值为 t 1 t_{1} t1、 t 2 t_{2} t2权值之和;④将 t t t加入 S S S集合。
返回保留在S中唯一一棵树
根据算法选择的N个代码的长度,找所有字符总数的最小值。
输入:输入包含多个测试用例,每个测试用例的第1行都包含一个整数 N ( 2 ≤ N ≤ 50 ) N(2 \leq N \leq 50) N(2≤N≤50),表示在文本中出现的不同字符数。第2行包含 N N N个整数 L i ( 1 ≤ L i ≤ 50 , i = 1 , 2 , . . . , N ) L_{i}(1 \leq L_{i} \leq 50,i = 1,2,...,N) Li(1≤Li≤50,i=1,2,...,N),表示由哈夫曼算法生成的不同字符的编码长度。假设至少存在一棵由上述算法构建的树,那么可以生成具有给定长度的编码。
输出:对每个测试用例都输出一行,表示所有字符总数的最小值。
在每一层都用一个深度数组,deep[]
记录该层节点的权值,将该层每个节点的权值都初始化为0,等待推测权值。
根据输入的编码长度算出最大长度,即哈夫曼树的最大深度maxd
。
从最大深度maxd
向上计算并推测,直到树根。开始时temp= 1
。
i = maxd
:第i
层的节点权值如果为0,则被初始化为temp
。对i层从小到大排序,然后将第i
层每两个合并,将权值放入上一层(i-1
层)。更新temp
为第i
层排序后的最后一个元素(最大元素)。
i = maxd-1
:重复上述操作。
i= 0
:结束,输出第0层第1个元素。
#include
#include
#include
using namespace std;
int main(){
int n,x;
while(cin>>n){
vector<long long>deep[n+1];
for(int i=0;i<n;i++)
deep[i].clear();
int maxd=0;
for(int i=0;i<n;i++){
cin>>x;
deep[x].push_back(0);
maxd=max(maxd,x);//求最大深度
}
long long temp=1;
for(int i=maxd;i>0;i--){
for(int j=0;j<deep[i].size();j++)
if(!deep[i][j])
deep[i][j]=temp;//将第i层最大的元素值赋值给i-1层没有权值的节点
sort(deep[i].begin(),deep[i].end());//第i层排序
for(int j=0;j<deep[i].size();j+=2)
deep[i-1].push_back(deep[i][j]+deep[i][j+1]);//合并后放入上一层
temp=*(deep[i].end()-1);//取第i层的最后一个元素,即第i层最大的元素
}
cout<<*deep[0].begin()<<endl;//输出树根的权值
}
return 0;
}
输入:
2
1 1
4
2 2 2 2
10
8 2 4 7 5 1 6 9 3 9
输入:
2
4
89
哈夫曼编码是一种最优编码方法。根据已知源字母表中字符的出现频率,将源字母表中的字符编码为目标字母表中的字符,最优的意思是编码信息的平均长度最小。在该问题中需要将 N N N个大写字母(源字母 S 1 . . . S N S_{1}...S_{N} S1...SN、频率 f 1 、 f N f_{1}、f_{N} f1、fN)转换成 R R R进制数字(目标字母 T 1 . . . T R T_{1}...T_{R} T1...TR)
输入:输入将包含一个或多个数据集,每行一个。每个数据集都包含整数值R( 2 ≤ R ≤ 10 2 \leq R \leq 10 2≤R≤10)、整数值N( 2 ≤ N ≤ 26 2 \leq N \leq 26 2≤N≤26)和整数频率 f 1 . . . f N f_{1} ...f_{N} f1...fN,每个都为1~999。整个输入数据都以 R R R为0结束,它不被认为是单独的数据集。
输出:对每个数据集都在单行上显示其编号(编号从1开始按顺序排列)和平均目标符号长度(四舍五入到小数点后两位)。然后显示 N N N个源字母和相应的哈夫曼代码,每行都有一个字母和代码。在每个测试用例后都打印一个空行。
#include
#include
#include
#include
#include
using namespace std;
struct node{
int freq,va,id; //频率,优先值,序号
node(int x = 0, int y = 0, int z = 0){ //构造函数
freq = x;
va = y;
id = z;
}
bool operator < (const node &b) const{ //重载运算符<
if(freq == b.freq){
return va>b.va;
}
return freq>b.freq;
}
};
const int maxn = 100;
int R,N; //基数,字母个数
int n,c; //补虚拟字母后的个数,重新生成字母编号
int fre[maxn],father[maxn],code[maxn];
priority_queue<node>Q; //优先队列
int main(){
int cas = 1;
while(cin >> R && R){
cin >> N;
memset(fre,0, sizeof(fre)); //初始化数组fre
int total = 0;
for (int i = 0; i < N; ++i) {
cin >> fre[i];
}
n = N;
while((n-R) % (R-1) != 0){ //补虚拟节点
n++;
}
while(!Q.empty()){ //优先队列清空
Q.pop();
}
for (int i = 0; i < n; ++i) { //将所有节点都入队
Q.push(node(fre[i],i,i));
}
c = n; //重新合成节点编号
int rec = 0; //统计所有频率和值
while(Q.size()!=1){ //构建哈夫曼树,剩余一个节点停止合并
int sum = 0,minva = n;
for (int i = 0; i < R; ++i) {
sum += Q.top().freq; //统计频率和
minva = min(Q.top().va,minva); //求最小优先级
father[Q.top().id] = c; //记录双亲
code[Q.top().id] = i; //记录编码
Q.pop(); //出队
}
Q.push(node(sum,minva,c)); //新节点入队
c++;
rec+=sum;
}
c--;
printf("Set %d; average length %0.2f\n",cas,1.0*rec/total);
for (int i = 0; i < N; ++i) { //哈夫曼编码
int cur = i;string s;
while(cur!=c){
s.push_back(code[cur]+'0');
cur = father[cur];
}
reverse(s.begin(),s.end()); //翻转编码,转换为从根到叶子编码
cout << " "<<char('A' + i) << ": " << s << endl;
}
cout << endl;
cas++;
}
return 0;
}
输入:
2 5 5 10 20 25 40
2 5 4 2 2 1 1
3 7 20 5 8 5 12 6 9
4 6 10 23 18 25 9 12
0
输出:
Set 1; average length 2.10
A: 1100
B: 1101
C: 111
D: 10
E: 0
Set 1; average length 2.20
A: 11
B: 00
C: 01
D: 100
E: 101
Set 1; average length 1.69
A: 1
B: 00
C: 20
D: 01
E: 22
F: 02
G: 21
Set 1; average length 1.32
A: 32
B: 1
C: 0
D: 2
E: 31
F: 33