中北大学软件学院算法分析与设计实验报告.docx
《中北大学软件学院算法分析与设计实验报告.docx》由会员分享,可在线阅读,更多相关《中北大学软件学院算法分析与设计实验报告.docx(38页珍藏版)》请在冰豆网上搜索。
中北大学软件学院算法分析与设计实验报告
北大学软件学院
实验
方向:
软件测试与开发(偏开发)
课程名称:
算法设计与分析
2016年3月制
1.实验名称
2016年4月7日8时至10时
学时数
实验一串匹配程序设计
2.实验目的
(1)熟练掌握串匹配的含义
(2)掌握BF算法匹配的过程并编程实现
(3)熟悉C++编译环境的基本操作
3.实验内容
给定两个字符串S和T,用BF算法,在主串S中查找字串T,输出结果,输出时要求有文字说明。
请编写程序。
4.实验原理或流程图
基本思想:
从主串S的第一个字符开始和模式T的第一个字符进行比较,若相等,则继续比较两者的后续字符;若不相等,则从主串S的第二个字符开始和模式T
的第一个字符进行比较,重复上述过程,若T中的字符全部比较完毕,则说明本趟匹配成功;若最后一轮匹配的起始位置是n-m,则主串S中剩下的字符不足够
匹配整个模式T,匹配失败。
这个算法称为朴素的模式匹配算法,简称BF算法。
设串S长度为n,串T长度为m,在匹配成功的情况下,考虑最坏情况,即每趟不成功的匹配都发生在串T的最后一个字符。
设匹配成功发生在si处,则在i-1趟不成功的匹配中共比较了(i-1)xm次,第i趟成功的匹配共比较了m次,所以总共比较了ixm次,
n_m-H
sPix(i
irn
最坏情况是O(nxm)o
或者写书上P39页的伪代码描述。
5.实验过程或源代码
#inelude
intmain(){
charS[100],T[100];
S(不超过100个字符):
");
printf("请输入主串
scanf("%s",S);
T(不超过100个字符):
");
printf("请输入子串
scanf("%s",T);
intindex=BF(S,T);
if(index==0){
printf(”模式匹配失败!
");
}
else{
printf(”模式匹配成功,子串%s在主串%s中首次匹配的位置
是%d",T,S,index);
}
return0;
}
6.实验结论及心得
通过本次实验我理解了使用蛮力法解决问题的特点,蛮力法的设计思想是直接基于问题本身
的描述来设计算法,即不采用任何方法来精简计算过程、运算次数或者设法简化问题本身。
所以蛮力法设计的算法虽然简单易懂,但是效率却往往不是很令人满意,比如串的模式匹配
问题中采用BF算法效率低下的主要原因就在于BF算法一旦主串和子串匹配失败就会产生回溯,如果能利用已有的匹配结果来减少回溯就可以提高效率,如KMP算法。
1.实验名称
2016年4月7日8时至10时
学时数
实验二排序冋题程序设计
2.实验目的
(1)掌握选择排序和起泡排序的基本思想
(2)掌握两种排序方法的具体实现过程
(3)在掌握的基础上编程实现两种排序方法
3.实验内容
输入一个待排序的序列,分别用选择排序和起泡排序两种排序方法将其变换成有序的序列,输出结果,输出时要求有文字说明。
请编写程序。
4.实验原理或流程图
书上P42页选择排序想法三点抄上去
书上P43页冒泡排序想法三点抄上去
5.
选择排序
实验过程或源代码
//
#inelude
//对含有n个数的数组进行遍历
voidvisit(intr[],intn){
for(inti=0;iprintf("%4d",r[i]);
printf("\n");
}
//选择排序
voidSelectSort(intr[],intn){
inti,j,index,temp;
//对n个记录进行n-1趟选择排序
intcompare=0,move=0;
for(i=0;i在无序区中查找最小记录
index=i;
for(j=i+1;jif(r[j]index=j;
compare++;//比较次数增加1
交换记录
}
if(index!
=i){//
temp=r[i];
r[i]=r[index];
r[index]=temp;
move+=3;//交换一次移动3次
}
visit(r,10);
}
printf(”在本次排序中共比较了%d次,移动了%d次。
\n",compare,move);
}
visit(r,10);return0;
选择排序
}
//
#include
//对含有n个数的数组进行遍历
voidvisit(intr[],intn){
for(inti=0;iprintf("%4d",r[i]);
printf("\n");
}
voidBubbleSort(intr[],intn){
intcount1=0,count2=0;
intbound,exchange=n-1;
while(exchange!
=0){
bound=exchange;
exchange=0;
for(intj=0;jif(++count1&&r[j]>r[j+1]){inttemp=r[j];
//count1
//
//
j++)
//
//
r[j]=r[j+1];r[j+1]=temp;
count2=count2+3;exchange=j;//
//1
和count2记载比较次数和移动次数第一趟起泡排序的区间是[0,n-1]当上一趟排序有记录交换时
一趟起泡排序区间是[0,bound]
注意不能写作count1++
次交换是3次移动操作记载每一次记录交换的位置
}
visit(r,10);//
}
printf(”本次排序中的比较次数为%d,移动次数为%c。
\n”,count1,count2);
}
每趟排序输出一次,
观察记录的变动情况
intmain(){
intr[10]={0};
printf(”请依次输入10个整数,用空格隔开:
\n");
for(inti=0;i<10;i++)
scanf("%d",&r[i]);
printf(”排序之前的记录:
visit(r,10);
printf("进行冒泡排序:
BubbleSort(r,10);
printf(”排序之后的记录:
visit(r,10);
\n");
(每趟排序后的结果如下所示)\n");
\n");
return0;
}
6.实验结论及心得
通过本次实验我理解了选择排序和起泡排序的基本思想。
选择排序和起泡排序都是通过将待
排序记录划分成有序区和无序区,然后通过交换扩大有序区,缩小无序区,最终达到排序的
目的。
两者又有区别,选择排序可以直接选出无序区的最小记录并一次插入到合适的位置上,之后不再调整,而冒泡排序则是通过两两交换实现移动的,由于很多移动无法将记录一次放
置到合适的位置上,所以存在很多“无效”的移动操作,从实验结果可以看出,起泡排序的移动次数明显比选择排序要多就是因为这样的“无效”的移动操作浪费了时间。
1.实验名称
2.
实验目的
2016年4月7日8时至10时
实验三数字旋转方阵程序设计
学时数
(1)
⑵
⑶
⑷
掌握分治法的设计思想
掌握数字旋转方阵的具体实现过程
熟练掌握二维数组的使用方法
在掌握的基础上编程实现数字旋转方阵的实现过程
3.实验内容
给出一个初始数据,在此数据的基础上由外层向里层填写数据,完成一个数字旋转方阵,输出结果,输出时要求有文字说明。
请编写程序。
4.实验原理或流程图
用二维数组data[N][N]表示Nxn的方阵,观察方阵中数字的规律,可以从外层向里层填数。
设变量size表示方阵的大小,则初始时size=N,填完一层则size=size-2;
设变量begin表示每一层的起始位置,变量i和j分别表示行号和列号,则每一层初始时i=begin,j=begin。
将每一层的填数过程分为A、B、C、D四个区域,则每个区域需要填写size-1个数字,填写区域A时列号不变行号加1,填写区域B时行号不变列号加1,填写区域C时列号不变行号减1,填写区域D时行号不变列号减1。
显然,递归的结束条件是size等于0或size等于1。
【写不下就算了】数字旋转方阵算法描述:
输入:
当前层左上角要填的数字number,左上角的坐标
输出:
数字旋转方阵
如果size等于0,如果size等于1,初始化行、列下标
重复下述操作size
begin,方阵的阶数size
1.
2.
3.
4.
4.1data[i][j]=number;number++;
5.重复下述操作size
5.1data[i][j]=number;number++;
6.重复下述操作size
则算法结束;
贝Udata[begin][begin]=numberi=begin,j=begin
-1次,填写区域
4.2
,算法结束;
行下标i++;
列下标不变;
-1次,填写区域
5.2
-1次,填写区域
6.2
6.1data[i][j]=number;number++;
7.重复下述操作size-1次,填写区域
7.1data[i][j]=number;number++;7.2
8.调用函数Full在size-2阶方阵中左上角begin+1
行下标不变;
行下标i--;
列下标j++;
列下标不变;
行下标不变,列下标j--;
处从数字number开始填数;
5.实验过程或源代码
#inelude
intdata[100][100]={0};
intmaxsize=0;
voidprintData(intsize){
for(inti=0;ifor(intj=0;jprintf("%4d",data[i][j]);printf("\n");
}
printf("\n");
}
voidFull(intnumber,intbegin,intsize){//从numberinti,j,k;
if(size==0)//return;
if(size==1){
开始填写size阶方阵,左上角的下标为(begin,begin)
递归的边界条件,如果size等于0,则无须填写
//
data[begin][begin]=number;//
printData(maxsize);return;
}
i=begin;j=begin;//
for(k=0;kdata[i][j]=number;number++;i++;
}
for(k=0;kdata[i][j]=number;
number++;j++;
}
for(k=0;kdata[i][j]=number;
number++;i--;
}
for(k=0;k}
printData(maxsize);
Full(number,begin+1,size-2);//
}
递归的边界条件,如果size
则只须填写number
等于1
//
//
//
//
//
//
//
//
//
//
//
初始化左上角下标
填写区域A,共填写size-1
在当前位置填写number
行下标加1
填写区域B,共填写size-1在当前位置填写number列下标加1
填写区域C,共填写size-1在当前位置填写number行下标减1
填写区域D,共填写size-1在当前位置填写number
列下标减1
个数
个数
个数
个数
递归求解,左上角下标为begin+1
intmain(){
intsize;
printf(”输入方阵的大小:
”);
scanf("%d",&size);
maxsize=size;
printf(”开始填充之前的数字旋转方阵:
\n");
printData(maxsize);
printf(”填充过程:
\n");
Full(1,0,size);
printf(”最终填充完成的结果是:
\n");
printData(maxsize);
return0;
}
6.实验结论及心得
通过本次实验,我理解了分治法解决问题的基本思想和一般过程,分治法的基本思想是而治之”将大的复杂的问题分解成结构同、规模小的子问题,分解子问题要足够小以至于可以直接得出子问题的解,然后对子问题的解进行合并,最终得到原问题的解。
由于程序中采用了递归技术,需要设置递归终止的条件,在数字旋转方阵问题中,递归结束的条件是
size等于0或者1。
递归问题的解决分为回溯和递推两个阶段,通过这两个过程可以求解本次实验的问题。
1.实验名称
实验四排序中分治法的程序设计
(1)
⑵
⑶
掌握归并排序和快速排序的划分方法掌握归并排序和快速排序的具体分治策略在掌握的基础上编程两种排序方法的实现过程
3.实验内容
给出一个初始序列,分别用归并排序和快速排序两种分治法将所给序列变换为有序序列,出结果,输出时要求有文字说明。
请编写程序。
4.实验原理或流程图
二路归并排序的分治策略是:
(1)划分:
将待排序序列r1,r2,…,rn划分为两个长度相等的子序列r1,…,rn/2
和rn/2+1,…,rn;
(2)求解子问题:
分别对这两个子序列进行排序,得到两个有序子序列;
(3)合并:
将这两个有序子序列合并成一个有序序列。
快速排序的分治策略是:
(1)划分:
选定一个记录作为轴值,以轴值为基准将整个序列划分为两个子序列r1…
ri-1和ri+1…rn,前一个子序列中记录的值均小于或等于轴值,后一个子序列中记录的值均大于或等于轴值;
(2)求解子问题:
分别对划分后的每一个子序列递归处理;
(3)合并:
由于对子序列r1…ri-1和ri+1…rn的排序是就地进行的,所以合并不需要执行任何操作。
【写不下就不写了】以第一个记录作为轴值,对待排序序列进行划分的过程为:
(1)初始化:
取第一个记录作为基准,设置两个参数i,j分别用来指示将要与基准记录进行比较的左侧记录位置和右侧记录位置,也就是本次划分的区间;
(2)右侧扫描过程:
将基准记录与j指向的记录进行比较,如果j指向记录的关键码大,
则j前移一个记录位置。
重复右侧扫描过程,直到右侧的记录小(即反序),若ivj,则将
基准记录与j指向的记录进行交换;
(3)左侧扫描过程:
将基准记录与i指向的记录进行比较,如果i指向记录的关键码小,
则i后移一个记录位置。
重复左侧扫描过程,直到左侧的记录大(即反序),若ivj,则将
基准记录与i指向的记录交换;
(4)重复
(2)(3)步,直到i与j指向同一位置,即基准记录最终的位置。
5.实验过程或源代码
//
#include
归并排序
//对含有n个数的数组进行遍历
voidvisit(intr[],intn){
for(inti=0;iprintf("%4d",r[i]);
printf("\n");
}
voidMerge(intr[],intr1[],ints,intm,intt){//inti=s,j=m+1,k=s;
printf("合并子序列r[%d]~r[%d],r[%d]~r[%d]
printf("r:
");visit(r,10);
while(i<=m&&j<=t){if(r[i]<=r[j])//r1[k++]=r[i++];
else
合并子序列
:
\n”,s,m,m+1,t);
<=
取r[i]和r[j]中较小者放入r1[k]
r1[k++]=r[j++];
}
while(i<=m){//r1[k++]=r[i++];
}
while(j<=t){//r1[k++]=r[j++];
}
printf("r1:
");visit(r1,10);
}
若第一个子序列没处理完,则进行收尾处理
若第二个子序列没处理完,则进行收尾处理
voidMergeSort(intr[],ints,intt){
intm;
intr1[1000]={0};
if(s==t)return;//
else{
m=(s+t)/2;//
printf("将序列r[%d]~r[%d]行求解:
\n",s,t,s,m,m+1,t);
MergeSort(r,s,m);
MergeSort(r,m+1,t);Merge(r,r1,s,m,t);for(inti=s;i<=t;i++)
递归的边界条件
划分
划分成r[%d]~r[%d]
、、[%d]~r[%d]两个子序列进
//
//
//
归并排序前半个子序列归并排序后半个子序列
求解子问题1,
求解子问题2,合并解,合并相邻有序子序列
r[i]=r1[i];
intmain(){
intr[10]={0};
printf(”请依次输入10个整数,用空格隔开:
\n");
for(inti=0;i<10;i++)
scanf("%d",&r[i]);
printf(”排序之前的记录:
\n");
visit(r,10);
MergeSort(r,0,9);
\rr);
printf("归并排序之后的记录:
printf("r:
”);visit(r,10);return0;
快速排序
}
//
#inelude
//对含有n个数的数组进行遍历
voidvisit(intr[],intn){
for(inti=0;iprintf("%4d",r[i]);
printf("\n");
}
//
intPartition(intr[],intfirst,intend){
inti=first,j=end;//
while(iwhile(iif(iinttemp=r[i];r[i]=r[j];r[j]=temp;//i++;
printf("r:
");visit(r,10);
}
while(iinttemp=r[i];r[i]=r[j];r[j]=temp;//j--;
printf("r:
");visit(r,10);
}
}
returni;//
初始化待划分区间
返回轴值记录的位置
划分
右侧扫描
左侧扫描
将较小记录交换到前面
将较大记录交换到后面
快速排序
voidQuickSort(intr[],intfirst,intend){//
intpivot;
if(firstpivot=Partition(r,first,end);//划分,pivot是轴值在序列中的位置
printf(”将序列r[%d]~r[%d]划分成r[%d]~r[%d]、、[%d]~r[%d]两个子序列进
行求解,轴值是r[%d]=%d.\n\rr,first,end,first,pivot-1first:
pivot-1,pivot+1,end,pivot,r[pivot]);
printf("r:
”);visit(r,10);
求解子问题1,对左侧子序列进行快速排序求解子问题2,对右侧子序列进行快速排序
QuickSort(r,first,pivot-1);//
QuickSort(r,pivot+1,end);//
intmain(){
intr[10]={0};
printf(”请依次输入10个整数,用空格隔开:
\n");
for(inti=0;i<10;i++)
scanf("%d",&r[i]);
printf(”排序之前的记录:
\n");
printf("r:
");visit(r,10);
printf("\n选取r[0]=%d作为轴值进行第一趟快速排序:
\n",r[0]);
QuickSort(r,0,9);
printf(”快速排序之后的记录:
\n");
printf("r:
");visit(r,10);
return0;
}
6.实验结论及心得
通过本次实验,我理解了归并排序和快速排序的基本思想,两者都是将序列分为若干子序列,
通过对子序列的排序,合并子序列,最终得到一个有序序列,这体现了分治法“分而治之”的思想。
这两种排序方法划分子序列的方式有所不同,归并排序按记录在序列中的位置进行划分,
快速排序则按照记录的值对序列进行划分,这就是两种排序方法在划分子序列上的不同之处。
1.实验名称
实验五汉诺塔问题的程序设计
(1)
⑵
⑶
掌握递归的有关概念
掌握汉诺塔问题的具体求解过程
在掌握的基础上编程实现汉诺塔的具体实现过程
3.实验内容
在A上有按大小排序好的n个金碟,借助B的帮助,将A上的碟子移动到C上,在移动的过程中要严格按照大小顺序,不能将碟子放在比它小的上面,输出结果,输出时要求有文字说明。
请编写程序。
4.实验原理或流程图
汉诺塔问题可以通过以下三个步骤实现:
(1)将塔A上的n-1个碟子借助