题目:汇编语言实现排序算法。先划分一块内存区域,存有10000个数据。
① 任选一种排序算法实现将这10000个数据从小到大排序;
② 快速搜索指定的数据,并统计其个数。
解:
对于第一问,简单的实现思路就可以考虑用冒泡排序。
冒泡排序是基于交换排序的思路,通过在无序区中相邻元素关键字的比较和位置的交换,最终达到排序的目的。其C语言实现如下:
for(int i=0;i<10000;i++)
for(int j=i+1;j<10000;j++)
{
if(a[i]<a[j])
{
int t=a[i];
a[i]=a[j];
a[j]=t;
}
}
假设数据段中存放以BUF开始的10000个字节类型的数据,其汇编实现从小到大排序如下:
ASSUME CS:CODE,DS:DATA
DATA SEGMENT
BUF DB 10000 DUP(?);划分内存区域,buf开始的10000个字节类型的数据,则一个存储单元的
;高四位和低四位各存放一个数据
DATA ENDS
CODE SEGMENT
START:
MOV AX,DATA
MOV DS,AX
LEA BX,BUF;取数据区域的首地址
MOV CX,10000;控制循环次数10000
XOR SI,SI;将SI清零
XOR DI,DI;将DI清零
L1:MOV AH,[BX+SI];用基变址寻址取操作数,L1为外循环,(SI)为循环变量,相当于i
L2:MOV AL,[BX+DI]; L2为内循环,(DI)为循环变量,相当于j
CMP AH,AL
JAE L3
MOV DH,AH; AH<AL,交换两个数
MOV AH,AL
MOV AL,DH
MOV [BX+DI],AL;将交换后的数存入存储器
MOV [BX+SI],AH;
L3:INC DI; AH>=AL不需交换,(AH)直接和后一个数比较,相当于j++
CMP DI,10000;判断内存循环是否结束
JB L2;未结束,则继续循环
;内层循环结束
INC SI;外层变量SI加一,相当于i++
MOV DI,SI;相当于j=i
LOOP L1
MOV AX,4C00H;返回操作系统
INT 21H
CODE ENDS
END START
对于第二问,搜索指定的数据,并给出其出现的次数。不考虑效率的前提下,最直观的思路就是遍历该内存区域,找到待查找的数值后再统计其个数。其汇编语言实现如下:
ASSUME CS:CODE,DS:DATA
DATA SEGMENT
BUF DB 10000 DUP(?);假定内存区域中以buf开始的10000个字节类型的数据已经排好序
DATA ENDS
CODE SEGMENT
START:
MOV AX,DATA
MOV DS,AX
;---------------------------------------------
;假定待查找的元素由用户输入,且为一个2位的16进制数据
MOV AH,1;输入第一个字符
INT 21H
MOV AH,16;十六进制数
SUB AL,'0'
MUL AH
MOV DH,AL
MOV AH,1;输入第二个字符
INT 21H
SUB AL,'0'
ADD DH,AL;最后的十进制数存放在DH中
MOV AH,1;接收回车符<enter>
INT 21H
MOV DL,10;输出换行
MOV AH,2
INT 21H
;---------------------------------------------
LEA DX,BUF;取数据区域的首地址
MOV AH,10
INT 21H
MOV CL,BUF+1;
XOR CH,CH
LEA SI,BUF+2
XOR DL,DL;DL清零, ;统计个数记录在DL中
AGAIN:
CMP AL,[SI]
JNZ NEXT
INC DL
NEXT:INC SI
LOOP AGAIN
ADD DL,30H
MOV AH,2
INT 21H
MOV AX,4C00H;返回操作系统
INT 21H
CODE ENDS
END START
很明显,该方法的效率比较低的。
如果仅仅是快速搜索数据,用二分查找就可以解决问题,关键问题如何用二分查找的方法统计该数字出现的次数。这里可以考虑用二分查找法进行两次查找,分别确定要查询的数的下标的上届和下届,则其个数等于上届减去下届加1.其C语言实现如下:
int find_lowest(int *a, int l, int r, int key)//a[l...r]
{
int mid, lowest;
int isfind = 0;
while(l <= r)
{
mid = (l+r)/2;
if (a[mid] == key)
{
isfind = 1;
lowest = mid;//find if there is a lower one
r = mid - 1;
}
else if (a[mid] > key)
{
r = mid - 1;
}
else
{
l = mid + 1;
}
}
if(isfind)
return lowest;
else
return -1;
}
int find_highest(int *a, int l, int r, int key)
{
int mid, highest;
int isfind = 0;
while(l <= r)
{
mid = (l+r)/2;
if (a[mid] == key)
{
isfind = 1;
highest = mid;
l = mid + 1;
}
else if (a[mid] > key)
{
r = mid - 1;
}
else
{
l = mid + 1;
}
}
if(isfind)
return highest;
else
return -1;
}
如果要在数组a中快速查找指定的元素x,令
x1=find_lowest(a, 0, 9999, x),
x2=find_lowest(a, 0, 9999, x),
则待查找的元素x出现的次数为x2-x1+1.
下面考虑用汇编语言实现二分查找, 实现如下:
ASSUME CS:CODE,DS:DATA
DATA SEGMENT
BUF DB 10000 DUP(?);假定内存区域中以buf开始的10000个字节类型的数据已经排好序
DATA ENDS
CODE SEGMENT
START:
MOV AX,DATA
MOV DS,AX
;---------------------------------------------
;假定待查找的元素由用户输入,且为一个2位的16进制数据
MOV AH,1;输入第一个字符
INT 21H
MOV AH,16;十六进制数
SUB AL,'0'
MUL AH
MOV DH,AL
MOV AH,1;输入第二个字符
INT 21H
SUB AL,'0'
ADD DH,AL;最后的十进制数存放在DH中
MOV AH,1;接收回车符<enter>
INT 21H
MOV DL,10;输出换行
MOV AH,2
INT 21H
;---------------------------------------------
LEA BX,BUF;取数据区域的首地址
MOV CL,BYTE PTR[BX+9999] ;基址寻址,CL中存放最大值
MOV AL,BYTE PTR[BX];AL中存放最小值
XOR CH,CH;清零
L1:
CMP AL,CL;
JG L3;
MOV DI,AX;实现mid=(max+min)/2
ADD DI,CX
SAR DI,1
CMP DH,BYTE PTR[BX+DI];key和a[mid]的比较
JZ L4;找到
CMP DH,BYTE PTR[BX+DI];key<mid
JB L2
INC DI;min=mid+1
MOV AX,DI
JMP L1
L2: DEC DI;max=mid-1
MOV CX,DI
JMP L1
L3: MOV DL,'N’;存入DL,N表示不存在该值
JMP L5
L4: MOV DL,'Y';Y表示存在该值
L5: MOV AH,2;输出结果
INT 21H
MOV AX,4C00H;返回操作系统
INT 21H
CODE ENDS
END START
以上汇编程序可能部分地方有点小问题,但是大题思路还是对的O(∩_∩)O~呵呵,二分查找的实现待完善