Hierarchical nonlinear structure - tree 05

Threaded binary

Achieve binary list of clues

[Program] before adding a subsequent chemotactic pointer field
may be increased before the binary nodes in the linked list pointer field to store information chemotaxis and successor node, wherein the node address before chemotaxis Lthread expressed, RThread represents address of the next node.
Here Insert Picture Description
Here Insert Picture Description
Preorder traversal of the tree, the child's relationship with the successor node is: either a successor node is its left child; if the child is left empty, for the right child; if the right child is empty, for the successor.

Considering the preorder traversal, the successor node and the child is the same, so only one child node of the target domain can be considered the original node the pointer field to store predecessor, successor clues.
[Scheme II] using the original node pointer fields
Here Insert Picture Description
by the right child pointer field, the storage address and the subsequent right child clues;
using left child pointer field, the storage address and precursors left child clues.

Considering the embodiment with the pointer field of the original node is unable to distinguish between a child or a clue;
To solve this problem, the corresponding flag bit attribute field to indicate the data pointer, such as 0 for a child, a cue representative of
Here Insert Picture Description
presence node has left child can not record its precursor conditions, as shown in the junction point E, D does not address its predecessor pointer field record, but for preorder, as long as there is a clue to the subsequent node, therefore, the precursor clues "break" does not affect the preorder traversal operation.

Binary scan process in some binary traversal order, or adding the precursor for each subsequent clue clue called node of binary tree, a clue is referred to increase threaded binary. Threaded object is to simplify and accelerate identification of predecessor nodes in the binary tree and subsequent speed, and after the binary tree traversal threaded convenient, does not require recursion, high process efficiency.
Here Insert Picture Description
Here Insert Picture Description
How to find a successor and predecessor node node in the binary tree traversal sequence of clues?
Here Insert Picture Description

Here Insert Picture Description
Threaded Method of summary

Threaded process - null pointer modified during traversal

由于线索化的实质是将二叉链表中的空指针改为指向结点前驱或后继的线索,而一个结点的前驱或后继结点的信息只有在遍历时才能得到,因此线索化的过程即为在遍历过程中修改空指针的过程。为了记下遍历过程中访问结点的先后次序,可附设一个指针 pre 始终指向刚刚访问过的结点。当指针 p 指向当前访问的结点时, pre 指向它的前驱。因此也可推知 pre 所指结点的后继为 p 所指的当前结点。这样就可在遍历过程中将二叉树线索化。对于找前驱和后继结点这两种运算而言,线索树优于非线索树。但线索树也有其缺点。在进行插入和删除操作时,线索树比非线索树的时间开销大。原因在于在线索树中进行插入和删除时,除了修改相应的指针外,还要修改相应的线索。

哈夫曼树(Huffman 树)及哈夫曼编码

哈夫曼树是带权路径长度最短的树,又称最优树,用途之一是构造通信中的压缩编码。先了解通信与编码的关系
Here Insert Picture Description
译码具有唯一性的必要条件
——任一个字符的编码都不是另一个字符的编码的前缀
Here Insert Picture Description
Here Insert Picture Description

哈夫曼树的概念
Here Insert Picture Description
Here Insert Picture Description
哈夫曼树构造算法

根据哈夫曼树的定义,一棵二叉树要使其 WPL 值最小,必须使权值越大的叶子结点越靠近根结点,而权值越小的叶子结点越远离根结点。
基本思想如下:
1)根据给定的 n 个叶子权值,可以看成是 n 棵只有一个根结点的二叉树,设 F 是由这 n 棵二叉树构成的集合;
2)在 F 中选取两棵根结点数值最小的树作为左、右子树,构成一棵新的二叉树,置新二叉树根的权值等于左、右子树根结点权值之和;
3)从 F 中删除这两棵树,并将新树加入 F;
4)重复2)、3),直到 F 中只含一棵树为止;
Here Insert Picture Description
哈夫曼树的算法实现

用优先队列构造哈夫曼树
Here Insert Picture Description

1)算法描述

使用优先队列(Priority Queue)来完成哈夫曼树的构造过程,设结点的权值即是它的优先级(Priority)。
(1)把 n 个叶子结点加入优先队列,则 n 个结点都有一个优先权Pi, 1<=i<=n
(2)如果队列内的结点数>1,则:
①从队列中移除两个最小的 Pi 结点;
②产生一个新结点,此结点为①之移除结点之父结点,而此结点的权重值为①两结点之权值和;
③把②产生之结点加入优先队列中。
(3)最后在优先队列里的点为树的根结点(root)。

2)数据结构描述

typedef struct 	//结点结构体
{
    char data;                  	//结点值
    int weight;                 	//权值
    int parent;                 	//双亲结点
    int lchild;                 	//左孩子结点
    int rchild;                 	//右孩子结点
} HTree_Node;       
typedef struct                      //编码结构体 
{
	char bit[N];                    //存放哈夫曼编码
	int start;                      //从start开始读bit中的哈夫曼码 
 } HuffmanCode_node;                 

3)函数框架设计:
Here Insert Picture Description
4)算法实现步骤图示
以权值分别为7、5、2、4的 4 个叶子为例,构造哈夫曼树步骤如图所示。
Here Insert Picture Description
(1)初态
初始化:将双亲、左右孩子三个指针均置为-1。
输入:读入 n=4 个叶子的权值存于向量的前 4 个分量中,它们是初始森林中 4 个孤立的根结点上的权值。
(2)合并过程
对森林中的树共进行 n-1 次合并,所产生的新结点依次放入向量 tree 的第 i 个分量中(n<=i<2*n-1)。每次合并分两步:
①在当前森林 tree[n] 的所有结点中,选取权最小和次小的两个根结点 tree[p1].weight 和 tree[p2].weight 作为合并对象,其中,0<=p1,p2<=i-1。
②将根为 tree[p1].weight 和 tree[p2].weight 的两棵树作为左右子树合并为一棵新的树。
双亲:新树的根是新结点 tree[i].weight。因此,应将 tree[p1].weight 和 tree[p2].weight 的 parent 置为 i,孩子:将 tree[i] 的 lchild 和 rchild 分别置为 p1 和 p2 ,权值:新结点 tree[i] 的权值应置为 tree[p1] 和 tree[p2] 的权值之和。

注意:合并后 tree[p1].weight 和 tree[p2].weight 在当前森林中已不再是根,因为它们的双亲指针均已指向 tree[i] ,所以下一次合并时不会被选中为合并对象。

程序实现

/*=======================================
函数功能:创建哈夫曼树
函数输入:(哈夫曼树)
函数输出:(哈夫曼树)
=========================================*/
void create_HuffmanTree(HTree_Node hTree[])
{
    	int i,k,lnode,rnode;
 	int min1,min2;
	printf("data weight parent lchild  rchild\n");
    	for (i=0;i<M;i++)       
        hTree[i].parent=hTree[i].lchild=hTree[i].rchild=-1; //置初值
 	for (i=N;i<M;i++)                   	//构造哈夫曼树
	{
        min1=min2=32767;     			//int的范围是-32768-32767
        lnode=rnode=-1;      			//lnode和rnode记录最小权值的两个结点位置
        for (k=0;k<=i-1;k++)
        {
			if (hTree[k].parent==-1)    		//只在尚未构造二叉树的结点中查找
			{
               	if (hTree[k].weight<min1)  	//若权值小于最小的左结点的权值
				{
                    	min2=min1;  rnode=lnode;
                    	min1=hTree[k].weight;lnode=k;
				}
                	else if (hTree[k].weight<min2)
				{
                    	min2=hTree[k].weight;rnode=k;
				}
			}
		}
  		//两个最小结点的父结点是I
  		hTree[lnode].parent=i;  hTree[rnode].parent=i; 
  		//两个最小结点的父结点权值为两个最小结点权值之和
  		hTree[i].weight=hTree[lnode].weight+hTree[rnode].weight;   
  		//父结点的左结点和右结点赋值
 		hTree[i].lchild=lnode;  hTree[i].rchild=rnode;   
	}
	for (i=0;i<M;i++) 
	{
	printf("%4c%6d%6d%6d%6d\n",hTree[i].data,hTree[i].weight,
  			hTree[i].parent,hTree[i].lchild,hTree[i].rchild);
	}
}

哈夫曼编码

In the Huffman tree has been established, parents path along the leaf nodes of the root node to fall back, step back each time, and passed through a Huffman tree branch, resulting in a Huffman code value Since Huffman coding sequence is 0, a character on the path from the root to the corresponding leaf node through which each branch consisting of, first branch code thus obtained is coded low ask code, obtained after the branch code is encoded by the high demand code.
Here Insert Picture Description

Algorithm Description:
(1) has been established in the Huffman tree, the leaf node L from the beginning to find their parents F, according to F, F L is determined left child or right child: left child is coded as 0, Right child is coded 1.
(2) Let L = F, the process is repeated until L is a root node, the coding is obtained from low to high.

Program realization

/*==========================================
函数功能:哈夫曼符号集编码
函数输入:哈夫曼树、(哈夫曼码编码表)
函数输出:(哈夫曼编码表)
=============================================*/
void create_HuffmanCode(HTree_Node hTree[], HCode_Node hCode[])
{
	int i,f,c;
	HCode_Node hc;
	for (i=0;i<N;i++)          //根据哈夫曼树求哈夫曼编码
	{
		hc.start=N;c=i;
		f=hTree[i].parent;
		while (f!=-1)          //循序直到树根结点结束循环
		{
			if (hTree[f].lchild==c)       //处理左孩子结点
				hc.bit[--hc.start]='0';
			 else                             //处理右孩子结点
				 hc.bit[--hc.start]='1';
			c=f;
			f=hTree[f].parent;
		}
		hCode[i]=hc;
	}
}

Huffman decoding
Here Insert Picture Description
algorithm description:

Start from the root node A, the message in accordance with their children looking B:
coded as 0, B is the left child;
coded as 1, B is a right child.
A = B is provided above process is repeated until a leaf node B.

The key is to find the same input data from an initial position from a set of digital encoding.
Program realization

/*====================================
函数功能:将哈夫曼码翻译为字符
函数输入:哈夫曼树、哈夫曼编码
函数输出:无
======================================*/
void decode_HuffmanCode(HTree_Node hTree[],HCode_Node hCode[])      
{
	char code[MAXSIZE];
	int i,j,k,m,x;

	scanf("%s",code);        		//把要进行译码的字符串存入code数组中
	while(code[0]!='#')
	for (i=0;i<N;i++)
	{
		m=0;                		//m为相同编码个数的计数器
 		for (k=hCode[i].start,j=0;  k<N;  k++,j++)  //j为记录所存储这个字符的编码个数
		{
			if(code[j]==hCode[i].bit[k]) m++;  //当有相同编码时m值加1
		}
		if(m==j)  //当输入的字符串与所存储的编码字符串个数相等		
	{
			printf("%c",hTree[i].data);
			for(x=0;code[x-1]!='#';x++)  //code数组用过的字符串删除
			{
				code[x]=code[x+j];
			}
		}
	}
}

Test function:

/*=======================================
函数功能:输出哈夫曼编码表
函数输入:哈夫曼树、哈夫曼码编码表
函数输出:(哈夫曼码编码表)
屏幕输出:哈夫曼树、哈夫曼码编码表
=========================================*/
void display_HuffmanCode(HTree_Node hTree[],HCode_Node hCode[])
{
 	int i,k;
 	printf("  输出哈夫曼编码:\n"); 
 	for (i=0;i<N;i++)               //输出data中的所有数据
 	{
 	printf("      %c:\t",hTree[i].data);               
 	for (k=hCode[i].start; k<N; k++)  //输出所有data中数据的编码
 	{
 	printf("%c",hCode[i].bit[k]);                     
 	}
 	printf("\n");
 	}
}

/*============================================
  函数功能:对给定字符串进行编码
  函数输入:哈夫曼树、哈夫曼码编码表
  函数输出:无
  键盘输入:要编码的字符串
  屏幕输出:输入字符串的哈夫曼编码串
===============================================*/
void edit_HuffmanCode(HTree_Node hTree[],HCode_Node hCode[])   
{
	char string[MAXSIZE];                        
	int i,j,k;
	scanf("%s",string);      			//把要进行编码的字符串存入string数组中
	printf("输出编码结果:\n");
	for (i=0;string[i]!='#';i++)        	//#为终止标志
	{
		for (j=0;j<N;j++)
		{
			if(string[i]==hTree[j].data)	//查找与输入字符相同的编号
			{
				for (k=hCode[j].start; k<N; k++)
				{
               		printf("%c",hCode[j].bit[k]);
				}
			}
		}
	}
}

Application examples Huffman tree - optimum decision algorithm

The preparation of a program that will convert into a percentile excellent, good, moderate, pass, fail five output levels.
Here Insert Picture Description
Here Insert Picture Description
Here Insert Picture Description
Here Insert Picture Description

Published 26 original articles · won praise 3 · Views 1468

Guess you like

Origin blog.csdn.net/herui7322/article/details/104277400