汇编32位实现哈夫曼编码(超详细)

一、题目要求

用汇编语言实现 Huffman 编码算法。要求输出给定字符集的 Huffman 编码及其 Huffman树的带权路径长度WPL,假设用于通信的电文仅由a、b、c、d、e、f、g、h等字母组成,字母在电文中出现的频率分别为:0.07、0.19、0.02、0.06、0.32、0.03、0.21、0.10。其WPL=2.61。

二、Huffman编码

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

至于Huffman树如何生成,见https://blog.csdn.net/qq_29519041/article/details/81428934

三、设计思路

通常用c语言来做这道题的话,我们第一时间想到的肯定是指针,用来指向它的左子树和右子树以及父节点,而在汇编中,并没有指针这个操作,于是乎,我想到了一个新的思路,类似于模拟一棵树。汇编语言中有一维数组,而一维数组足以解决这道题目了。

1、首先需要三个数组,parents数组,存放在生成Huffman树过程中,所有的父节点,lchird数组,存放在生成Huffman树过程中所有成为左孩子的节点,rchird同理。

2、对初始的频率(频率是小数,但我用了整数,只要能表达频率之间的大小关系即可)进行从小到大排序(我这里使用的是冒泡排序,因为简单实现)。特别提一下,加入可以直接找出最小的两个频率,不用排序当然也是可以的

3、将最小的两个频率,左小右大的规则放在左孩子数组和右孩子数组里面,将他们的和放到父亲数组里面,从这也就可以看出,父亲节点在数组中的位置和左孩子和右孩子是一样的,也就是所,他们可以直接通过自己的下标找到自己的父亲节点。然后将最小的两个删除,将和重新插入频率表中。

4.重复2,3,直到频率表中只有一个根节点的时候。

5、类似于下图,先找所求的叶子节点在哪个孩子数组,加入下面的图,黄色的叶子节点先在左孩子数组,因此给他的编码先加一个0,再找和他下标一样的父亲节点是谁,由图可以看到他的父亲节点是红色,接下来再看,红色属于还哪一个孩子数组,这里找的话,应该从孩子数组下标为1的地方开始找起(数组下标从0开始),因为和父亲下标一样或者比父亲下标小的左孩子和右孩子一定不是它本身。

6、这里看到红色的父节点又属于左孩子3,所有编码中再添加一个0,再重复5,6,直到找到的父节点在父亲数组的最后一个,也就是根节点,就结束查找。

7、将刚才的编码逆向输出。

四、汇编代码

include vcIO.inc
.data
WPL dword 0                           ;带权路径和初值为0
print_wpl byte '带权路径和WPL为:%d',10,0
Huffman_code dword 20 dup(?)
parents dword 20 dup(?)
rchird dword 20  dup(?)
lchird dword 20 dup(?)
r_point dword ?
l_point dword ?
char byte 'abcdef'
number dword 2,5,3,6,8,1
length_number dword ?
l_num dword 0                    ;左子树数组数量
r_num dword 0					 ;右子树数组数量
p_num dword 0                    ;父节点数组数量
now_num dword ?
len dword lengthof number
number1 dword 20 dup(?)
info_print byte '%c的哈夫曼编码是:',0,0
type_print byte '%d',0,0
speace_print byte ' ',10,0
temp dword ?
now_char dword ?
.code
main proc
mov length_number,lengthof number
mov ecx,lengthof number                                        ;复制一份number的副本
mov esi ,0
loop2:
	mov eax,number[esi*4]
	mov number1[esi*4],eax
	inc esi
loop loop2
mov ecx,lengthof number
loop_sort:                            ;循环最小的两个数
	call sort
	call add_array
	dec ecx
	cmp ecx,1
	ja loop_sort
	mov ecx ,len                          
	mov esi,0
loop_find:
	mov eax,number1[esi*4] 
	mov now_num,eax		                       ;寻找每一个字符的哈夫曼编码,左零右一	
	mov bl,char[esi]
	mov now_char,ebx
	call find_code
	inc esi
	loop loop_find
invoke printf ,offset print_wpl,WPL
ret
main endp
sort proc                                ;排序
	push ecx
	push eax
	push ebx
	push edx
	mov ecx ,0
	mov eax ,0
	mov ebx,1
	out_loop:                                 ;冒泡排序
		cmp ecx,length_number
		ja out1
		inter_loop:
			cmp ebx,length_number
			je	out2
			mov edx,number[ebx*4]
			cmp number[eax*4],edx
			jl not_exchange                              ;前面比后面大则交换
			xchg number[eax*4],edx
			xchg number[ebx*4],edx
			not_exchange:
				inc eax
				inc ebx
				jmp inter_loop
	out2:
		mov eax ,0
		mov ebx ,1
		inc ecx
		jmp out_loop
out1:	
    pop edx
	pop ebx
	pop eax
	pop ecx

ret
sort endp
add_array proc                         ;将属于父节点的加入父节点数组,将属于子节点的加入子节点数组
	push ecx
	push eax
	push ebx
	push edx
	mov ecx,l_num
	mov eax,r_num	
	mov edx ,number[4]
	mov ebx,number[0]
	mov  rchird[ecx*4], edx
	mov  lchird[eax*4], ebx
	add edx,ebx
	mov ecx ,p_num
	mov parents[ecx*4],edx
	inc l_num
	inc r_num                                   ;三个数组数量都加一
	inc p_num
	mov ecx,0                                  ;合并原始数组
	mov ebx,1
	loop1:
		cmp ebx,length_number
		ja out3
		mov eax,number[ebx*4]
		mov number[ecx*4],eax
		inc ecx
		inc ebx
		jmp loop1
		
	out3:
	mov number[0],edx
	dec length_number
	pop edx
	pop ebx
	pop eax
	pop ecx
ret
add_array endp
find_code proc
push ecx
push eax
push ebx
push esi
push edx
push now_num
	mov ebx,0
	mov ecx,p_num
	mov temp,ecx
	mov esi,0
	loop_code:
		mov eax,now_num
		
		loop_r:
			cmp rchird[esi*4] ,eax
			je nextr
			inc esi
			cmp esi,ecx
			jne loop_r
			mov esi,0
		loop_l:
			cmp lchird[esi*4] ,eax
			je nextl
			inc esi
			cmp esi,ecx
			jne loop_l
		nextr:
			mov Huffman_code[ebx*4],1
			jmp find_p
		nextl:
			mov Huffman_code[ebx*4],0
			find_p:
				inc ebx
				mov edx,parents[esi*4]
				mov now_num,edx 
				inc esi
				cmp esi,temp
				jb loop_code
pop now_num
pushad
invoke printf ,offset info_print,now_char
popad
mov ecx,ebx
mov eax ,ebx                    ;做乘法
mul now_num
add WPL ,eax
dec ebx
mov esi ,ebx
loop_print:
	pushad
	invoke printf ,offset type_print,Huffman_code[esi*4]
	popad
	dec esi
	loop loop_print 
	pushad
	invoke printf ,offset speace_print
	popad
pop edx
pop esi
pop ebx
pop eax
pop ecx
ret
find_code endp
end main

五,运行结果

输入:

char byte 'abcdef'
number dword 2,5,3,6,8,1

输出: 

a的哈夫曼编码是:0111
b的哈夫曼编码是:00
c的哈夫曼编码是:011
d的哈夫曼编码是:01
e的哈夫曼编码是:11
f的哈夫曼编码是:0110
带权路径和WPL为:59

六、总结

往往在遇到问题时,有两种办法,一种是克服,一种是绕过,c中的指针,解决这个问题很简单,但在汇编中,就应该想到用模拟构造一棵树来解决,有时候,换一种思路未尝不是一件好事。

初来乍到,文章可能会存在些许问题,希望大家能够及时指正批评,欢迎交流。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值