算法设计 实验 报告 K路归并 2路归并 单源点最短路径.docx
《算法设计 实验 报告 K路归并 2路归并 单源点最短路径.docx》由会员分享,可在线阅读,更多相关《算法设计 实验 报告 K路归并 2路归并 单源点最短路径.docx(15页珍藏版)》请在冰豆网上搜索。
算法设计实验报告K路归并2路归并单源点最短路径
《算法设计》实验报告
姓名:
xxx
学号:
U20091519x
班级:
0911
专业:
计算机科学与技术
报告日期:
2011/11/10
实验一(k路归并)
问题描述及分析:
给定k值和n个文件内的记录(即长度)q1,q2,q3,..qn,每次归并k个文件,找出最优归并模式。
由于归并一个具有x个记录的文件和一个具有y个文件时间复杂度为O(x+y),因此对于度量标准的一种明显选择是:
每一步归并权值最小的k个文件。
算法思想:
由最优二路归并树的思想,可以导出K路归并的贪心算法,即在每一步归并时,都选择k棵具有最小长度的子树(权值分别为q1,q2,q3,..qk)用于归并,用权值为q1+q2+q3+..+qk的新结点取代前K个结点,再与其它结点一起按此方法继续归并。
在此情况下,相应的归并树是一棵K元树。
由于所有的内部结点的度必须为K,因此对于n的某些值,就不与K元归并树相对应。
所以,有必要引入一定量的虚外部结点,且为不影响所产生k元树的带权外部路径长度,因此需将每个虚结点的权值赋0。
这样,最后得到的归并树一定是满k元树。
程序设计:
分两种情况,若k=2即二路归并时,不需要引入“虚”外部结点,每次只需将权值最小的结点两两归并。
若k≠2,根据满k元树的性质可知,仅当nmod(k-1)=1时,不需要引入“虚”外部结点;当nmod(k-1)=0时,需引入1个“虚”外部结点;其他情况需引入”虚”外部结点个数为k-(nmod(k-1))。
添加虚外部结点后,外部结点与内结点数的有如下关系关系:
inner_node=(outer_node-1)/(k-1)。
在本程序中,各文件的长度即权值,而且由于每次归并前,结点已经按权值递增的次序排列,所以每次归并仅需要将前K个元素归并即可。
源代码(见源代码merge.c):
#include
/*按照文件长度由小到大排序并显示*/
voidsort_show(intstart,intend,intk,int*s)
{/*start、end分别为数组首末元素的小标*/
inti,j,cur,temp;
for(i=start;i{
cur=i;
for(j=i+1;j<=end;j++)
{
if(s[j]
}
if(cur!
=i)
{
temp=s[cur];
s[cur]=s[i];
s[i]=temp;
}
}
if(0!
=(end-start))
{
for(i=start;i<=end;i++)
{/*将前k个元素用括号括起来*/
if(i==start)printf("(%d\t",s[i]);
elseif(i==start+k-1)printf("%d)\t",s[i]);
elseprintf("%d\t",s[i]);
}
}
elseprintf("%d",s[start]);
}
main()
{/*文件个数file_num,k路归并,内部结点数即归并次数inner_node,需要引入的“虚”外部结点数unreal_node,总的外部结点数(包括虚结点)outer_node*/
intfile_num,k,x,inner_node,outer_node,unreal_node;
inti,j,count,sum,*list;
printf("inputthenumberoffiles:
");
scanf("%d",&file_num);
printf("inputk:
");
scanf("%d",&k);
/*若k=2即二路归并时,不需要引入“虚”外部结点*/
if(2==k)unreal_node=0;
else
{/*若k!
=2时,仅当file_nummod(k-1)=1时,不需要引入“虚”外部
结点*/
x=file_num%(k-1);
if(0==x)unreal_node=1;
elseif(1==x)unreal_node=0;
elseunreal_node=k-x;
}
outer_node=unreal_node+file_num;
inner_node=(outer_node-1)/(k-1);
/*申请该数组用于存放各文件(包括引入的虚结点)的长度*/
list=(int*)malloc(outer_node*sizeof(int));
for(i=0;iprintf("\ninputlengthofeachfile:
\n");
for(i=unreal_node;i{
printf("Len[%d]=",i-unreal_node+1);
scanf("%d",&list[i]);
}
for(i=0,count=0;i<=outer_node-1;i=i+k-1)
{
printf("\n\nafter%dmerge:
\n",count);
sort_show(i,outer_node-1,k,list);/*将每次归并后的结果输出*/
sum=0;
if(outer_node-1!
=i)
{
for(j=count*(k-1);j<=(count+1)*(k-1);j++)
sum+=list[j];/*sum用于求取每次归并的结果*/
}
list[j-1]=sum;
count++;/*count计数归并的次数*/
}
getch();
}
测试实例:
给定8个文件,长度依次为3、2、5、1、6、9、12、8,请分别用2路归并和3路归并。
、分析:
归并树如下图所示(外部结点用方框表示,内结点用圆表示):
2路归并树3路归并树
46
46
11
26
20
23
12
14
11
12
9
3
3
5
6
8
9
6
8
6
5
3
3
0
1
2
2
1
、运行结果(输入k值、文件个数及个文件长度,输出每次归并后的结果):
1K=2即二路归并时,输入8个文件,长度依次为3、2、5、1、6、9、12、8。
2k≠2比如k=3时,输入8个文件,长度依次为3、2、5、1、6、9、12、8。
其中,每次输出结果中括号内的部分为下次需要归并的元素,最前面的0表示引入的“虚”外部结点。
实验二(单源点最短路径)
问题描述与分析:
已知一个n结点的有向图G=(V,E)和各边的权w(e),求由G中的某指定点v到其它个结点的最短路径。
为制定产生最短路径的贪心算法,对于这个问题应该想出一个多级解决办法和一种最优的量度标准。
算法思想:
设S表示对其已经生成了最短路径的那些结点(包含起点v)的集合。
对于不在S中的w,设dist[w]是从v开始只经过S中的结点而在w结束的那条最短路径的长度,则有以下结论:
①如果下一条最短路径是到结点u,则这条路径是从v开始而在u处终止,并且只通过了那些在S中的结点。
②所生成的下一条路径的终点必定是所有不在S内的结点中具有最小距离dist[u]的结点。
③在像②中那些选出了结点u并生成从v到u的最短路径后,结点就成为S中的一个成员。
此时,若dist[w]减小,那是由于有一条从v经u到w的更短路径,其中从v到u的路就是这样一条最短的路,而从岛的路径是边,且这条路径的长度是dist[]+w(u,w)。
程序设计:
假定G中的n个结点被标上0到n-1,若结点i在数组S中,则是[i]=1,若不在s中,则s[i]=0。
已给定该图的成本邻接矩阵,cost[i][j]是边的权,则在边不在E(G)中的情况下,cost[i][j]被置以某个无穷大的数+∞。
对于i=j时,cost[i][j]被置以0。
每次判断时,若dist[u]+cost[u][w]添加到w的路径path中。
当n个结点中有n-1个结点在S中时,即需要判断n-2次,算法才会终止。
源代码(见源代码path.c):
#include
#defineN7
#defineINFINITY32767/*定义无穷大‘+∞’*/
main()
{/*若结点i在S中,则s[i]=1,否则s[i]=0;dist[j]表示结点v到j最短路径长度,
并用path[][]保存路径*/
inti,j,k,n=N,v,u,w;
ints[N],dist[N],path[N][N];
intcost[N][N]={/*初始化成本邻接矩阵*/
{0,20,50,30,INFINITY,INFINITY,INFINITY},
{INFINITY,0,25,INFINITY,INFINITY,70,INFINITY},
{INFINITY,INFINITY,0,40,25,50,INFINITY},
{INFINITY,INFINITY,INFINITY,0,55,INFINITY,INFINITY},
{INFINITY,INFINITY,INFINITY,INFINITY,0,10,70},
{INFINITY,INFINITY,INFINITY,INFINITY,INFINITY,0,50},
{INFINITY,INFINITY,INFINITY,INFINITY,INFINITY,INFINITY,0}};
for(i=0;i{/*输出成本邻接矩阵,其中‘+’表示+∞*/
for(j=0;j{
if(INFINITY==cost[i][j])printf("%s","+\t");
elseprintf("%d\t",cost[i][j]);
}
printf("\n");
}
loop:
printf("\ninputstart_point:
");/*输入起点下标v*/
scanf("%d",&v);
if(v>=n||v<0)/*若输入越界,提示再次输入*/
{
printf("inputover_border!
!
\n");
gotoloop;
}
for(i=0;i{/*对数组s、dist和path进行初始化*/
s[i]=0;
dist[i]=cost[v][i];
if(INFINITY!
=dist[i])
{
path[i][0]=v;
path[i][1]=i;
path[i][2]=-1;
}
}
s[v]=1;/*首先将v计入S*/
dist[v]=0;
for(i=1;i{/*在S之外求取结点u,使得dist[u]最小*/
for(j=0;jif(0==s[j])
{
u=j;
break;
}
for(k=j+1;kif(0==s[k]&&dist[k]{
u=k;
break;
}
s[u]=1;/*将结点u计入S*/
for(w=0;w{
if(0==s[w]&&(dist[u]+cost[u][w]){/*判断,修改距离dist[],并用path[][]记录路径*/
dist[w]=dist[u]+cost[u][w];
for(i=0;-1!
=path[u][i];i++)path[w][i]=path[u][i];
path[w][i]=w;
path[w][i+1]=-1;
}
}
}
printf("\n");
for(i=0;iif(i!
=v)
{/*输出v到其它各节点的最短路径,若不可达,输出Notreachable*/
if(INFINITY==dist[i])printf("v[%d]-->v[%d]:
Notreachable!
",v,i);
else
{
printf("v[%d]-->v[%d]:
distance=%d",v,i,dist[i]);
printf("\tpath=");
for(j=0;-1!
=path[i][j];j++)
{
printf("v%d",path[i][j]);
if(-1!
=path[i][j+1])printf("->");
}
}
printf("\n");
}
getch();
}
测试实例:
求下图中V0到其余各个结点的最短路径。
V5
20
V0
V2
50
50
50
70
25
V1
V6
25
40
10
70
V3
30
55
V4
、分析:
最短路径算法执行追踪
迭代
选取的结点
S
dist[]
0
1
2
3
4
5
6
置初值
—
1
0
20
50
30
+∞
+∞
+∞
1
2
1,2
0
20
45
30
+∞
90
+∞
2
4
1,2,4
0
20
45
30
85
90
+∞
3
3
1,2,4,3
0
20
45
30
70
90
+∞
4
5
1,2,4,3,5
0
20
45
30
70
80
140
5
6
1,2,4,3,5,6
0
20
45
30
70
80
130
、运行结果(比如先输入8,会提示越界,再输入0,则正常显示):