算法设计王茂林.docx

上传人:b****4 文档编号:3782504 上传时间:2022-11-25 格式:DOCX 页数:20 大小:90.25KB
下载 相关 举报
算法设计王茂林.docx_第1页
第1页 / 共20页
算法设计王茂林.docx_第2页
第2页 / 共20页
算法设计王茂林.docx_第3页
第3页 / 共20页
算法设计王茂林.docx_第4页
第4页 / 共20页
算法设计王茂林.docx_第5页
第5页 / 共20页
点击查看更多>>
下载资源
资源描述

算法设计王茂林.docx

《算法设计王茂林.docx》由会员分享,可在线阅读,更多相关《算法设计王茂林.docx(20页珍藏版)》请在冰豆网上搜索。

算法设计王茂林.docx

算法设计王茂林

实验一递归与分治

姓名:

王茂林

班级:

信息1411

学号:

2014125104

指导老师:

潘老师

实验目的:

理解递归与分治算法设计思想和方法。

实验课时:

4学时

实验原理:

一个规模为n的复杂问题的求解:

可以划分成若干个规模较小

若划分成的每一个子问题都与原问题的性质相同,可用相同的求解方法;当子问题规模划分一定小时,子问题的解已知,则逆求原问题的解,这是递归的思想。

实验题目:

1、汉诺塔(hanoi)问题。

设有A、B、C共3根塔座,在塔座A上堆叠n个金盘,每个盘大小不同,只允许小盘在大盘之上,最底层的盘最大,如下图所示。

现在要求将A上的盘全都移到C上,在移的过程中要遵循以下原则:

每次只能移动一个盘;圆盘可以插在A、B和C任一个塔座上;在任何时刻,大盘不能放在小盘的上面。

 

hanoi问题递归求解思想:

我们把一个规模为n的hanoi问题:

1到n号盘按照移动规则从A上借助B移到C上表示为H(A,B,C,n);原问题划分成如下三个子问题:

(1)将1到n-1号盘按照移动规则从A上借助C移到B上H(A,C,B,n-1);

(2)将n号盘从A上直接移到C上;

(3)将1到n-1号盘按照移动规则从B上借助A移到C上H(B,A,C,n-1);

经过三个子问题求解,原问题的也即求解完成。

hanoi问题递归求解代码:

voidH(charA,charB,charC,intn)

{

if(n>0)

{

H(A,C,B,n-1);

printf(“%dfrom%cto%c”,n,A,C);

H(B,A,C,n-1);

}

}

-c语言实验代码:

#include

voidhanoi(intn,charX,charY,charZ)

{

if(n==1)

printf("把%c移动到%c\n",X,Z);

else

{hanoi(n-1,X,Z,Y);

printf("把%c移动到%c\n",X,Z);

hanoi(n-1,Y,X,Z);

}

}

main()

{

intm;

printf("请输入盘子的数目:

");

scanf("%d",&m);

printf("要移动的盘子执行的步骤为:

%d\n",m);

hanoi(m,'A','B','C');

}

实验结果:

 

2、二分查找问题

(1)设a[0:

n-1]是一个已排好序的数组。

请改写二分搜索算法,使得当搜索元素x不在数组中时,返回小于x的最大元素的位置i和大于x的最小元素位置j。

当搜索元素在数组中时,i和j相同,均为x在数组中的位置。

***********************************************************

代码显示:

#include

intbinarySearch(inta[],intn,intx,int&i,int&j)//a[]为要搜索的数组;n为数组元素的个数;x为要查询的元素值;

//i为小于x的最大元素位置;j为大于x的最小元素的位置

{

intmiddle;//中值

intright=n-1;//数组的右边界

intleft=0;//数组的左边界

while(left<=right)

{

middle=(left+right)/2;

if(x==a[middle])

{

i=j=middle;

returnmiddle;

}

if(x>a[middle])

left=middle+1;

else//2016年11月3日测试成功。

right=middle-1;

i=right;

j=left;

}

return-1;//查询失败

}

intmain(void)

{

inti;

intj;

intmiddle;

intb[]={1,2,3,4,5,6,7,8,12,22};//用来测试的数组

middle=binarySearch(b,10,13,i,j);//调用二分搜索算法

if(middle!

=-1)//查询成功

{

printf("thex'spositionis:

%d,iis%d,jis%d\n",middle,i,j);

}

else//查询失败

{

printf("找不到这个元素:

%d,jis%d\n",i,j);

}

return0;

}

结果显示:

方法二:

如果存在,必须返回下标

#include

usingnamespacestd;

constintSIZE=12;

intbinarySearch(intArray[],intx,intn,int&a,int&b)

{

intleft=0;

intright=n-1;

intmiddle;

while(left<=right)

{

middle=(left+right)/2;

if(x==Array[middle])

{

a=b=middle;

return0;

}

if(x>Array[middle])

left=middle+1;

else

right=middle-1;

}

a=right;

b=left;

return1;

}

intmain()//于16年11月3日测试成功

{

inta,b;

intt;

intArray[SIZE]={1,4,7,10,23,33,36,65,76,87,89,90};

cout<<"请输入要搜索的元素:

";

cin>>t;

if(binarySearch(Array,t,SIZE,a,b)==0)

{

cout<<"查找的元素存在!

"<

"<

}

else

{

cout<<"查找的元素不存在!

"<

<<"大于X的最小元素的位置为:

"<

<<"小于X的最大元素位置为:

"<

}

return0;

}

当输入元素存在时的下标显示:

当输入结果不存在时,回馈最大最小元素

 

(2)设有n个不同的整数排好序后存放于t[0:

n-1]中,若存在一个下标i,0≤i<n,使得t[i]=i,设计一个有效的算法找到这个下标。

要求算法在最坏的情况下的计算时间为O(logn)。

boolBinarySearch(inta[],intn,intx,int&i,int&j)

{

   intleft=0;

   intright=n-1;

   while(left

   {

      intmid=(left+right)/2;

      if(x==a[mid])

      {

         i=j=mid;

         returntrue;

      }

      if(x>a[mid])

         left=mid+1;

      else

         right=mid-1;

   }

   i=right;

   j=left;

   returnfalse;

}

 

intSearchTag(inta[],intn,intx)

{

   intleft=0;

   intright=n-1;

   while(left

   {

      intmid=(left+right)/2;

      if(x==a[mid])returnmid;

      if(x>a[mid])

         right=mid-1;

      else

         left=mid+1;

   }

   return-1;

*********************************************************************

代码实现:

#include

#defineN10

boolBinarySearch(inta[],intn,intx,int&i,int&j)

{

intleft=0;

intright=n-1;

while(left

{

intmid=(left+right)/2;

if(x==a[mid])

{

i=j=mid;

returntrue;

}

if(x>a[mid])

left=mid;

else

right=mid;

}

i=right;

j=left;

returnfalse;

}

intSearchTag(inta[],intn)

{

intleft=0;

intright=n-1;

while(left

{

intmid=(left+right)/2;

if(mid==a[mid])returnmid;

if(mid

right=mid;

else

left=mid;

}

return-1;

}

intmain(intargc)

{

inta[N];

printf("请输入%d个由小到大排列的整数\n以空格隔开,以回车结束\n",N);

intk;

for(k=0;k

scanf("%d",a+k);

printf("你的输入如下:

\n");

for(k=0;k

printf("%d\t",k);

//printf("\n");

for(k=0;k

printf("%d\t",a[k]);

printf("\n");

if((k=SearchTag(a,N))!

=-1)

printf("第%d个数字满足a[%d]=%d\n",k,k,k);

else

printf("没有找到任何一个数字满足a[i]=i\n");

inti,j;

intx;

printf("请输入你想要查找的数字\n");

scanf("%d",&x);

if(BinarySearch(a,N,x,i,j))

printf("找到了:

i和j均在%d\n",j);

else

printf("没找到:

j=%d\ti=%d\n",j,i);

getchar();

getchar();

return0;

}

 

3、快速排序问题

在快速排序中,记录的比较和交换是从两端向中间进行的,关键字较大的记录一次就能交换到后面单元,关键字较小的记录一次就能交换到前面单元,记录每次移动的距离较大,因而总的比较和移动次数较少。

typedefintType;

voidQuickSort(Typea[],intp,intr)

{

if(p

intq=Partition(a,p,r);

QuickSort(a,p,q-1);//对左半段排序

QuickSort(a,q+1,r);//对右半段排序

}

}

template

intPartition(Typea[],intp,intr)

{

inti=p,j=r+1;

Typex=a[p];

//将

//将>x的元素交换到右边区域

while(true){

while(a[++i]

while(a[--j]>x);

if(i>=j)break;

Swap(a[i],a[j]);

}

a[p]=a[j];

a[j]=x;

returnj;

}

**********************************************

快速排序代码实现:

#include

voidsort(inta[],intleft,intright)

{

if(left>=right)

return;

inti=left;

intj=right;

inttemp=a[left];

while(i!

=j)

{

while(temp

j--;

if(i==j)

break;

a[i]=a[j];

i++;

while(a[i]

i++;

if(i==j)

break;

a[j]=a[i];

j--;

}

a[i]=temp;

sort(a,left,i-1);

sort(a,i+1,right);

}

intmain()

{

intarr[7]={10,2,3,5,3,6,9};

intvvv[9]={5,532,523,532,87,3124,76,4325,6};

intk;

sort(arr,0,6);

for(k=0;k<7;k++)

printf("%d",arr[k]);

printf("\n");

sort(vvv,0,8);

for(k=0;k<9;k++)

printf("%d",vvv[k]);

printf("\n");

return0;

}

结果展示:

思考问题:

1.递归的关键问题在哪里?

答:

为防止递归的无休止调用,在递归函数中要及时返回,就是结束条件的作用。

从以上的程序我们刻印看到:

在所有的递归函数中都有一个终止递归的条件判断。

2.递归与非递归之间程序的转换?

通常,一个函数在调用另一个函数之前,要作如下的事情:

a)将实在参数,返回地址等信息传递给被调用函数保存;

b)为被调用函数的局部变量分配存储区;

c)将控制转移到被调函数的入口.

从被调用函数返回调用函数之前,也要做三件事情

:

a)保存被调函数的计算结果;

b)释放被调函数的数据区;

c)依照被调函数保存的返回地址将控制转移到调用函数.

   所有的这些,不论是变量还是地址,本质上来说都是"数据",都是保存在系统所分配的栈中的.

递归调用时数据都是保存在栈中的,有多少个数据需要保存就要设置多少个栈,而且最重要的一点是:

控制所有这些栈的栈顶指针都是相同的,否则无法实现      同步.

下面来解决第二个问题:

在非递归中,程序如何知道到底要转移到哪个部分继续执行?

回到上面说的树的三种遍历方式,抽象出来只有三种操作:

访问当前结点,访问左子树,访问右子树.这三种操作的顺序不同,遍历方式也不同.如果我们再抽象一点,对这三种操作再进行一个概括,可以得到:

a)访问当前结点:

对目前的数据进行一些处理;

b)访问左子树:

变换当前的数据以进行下一次处理;

c)访问右子树:

再次变换当前的数据以进行下一次处理(与访问左子树所不同的方式).

  下面以先序遍历来说明:

voidpreorder_recursive(BitreeT)           /*先序遍历二叉树的递归算法*/

      {

             if(T){

                    visit(T);              /*访问当前结点*/

                    preorder_recursive(T->;lchild); /*访问左子树*/

                   preorder_recursive(T->;rchild); /*访问右子树*/

             }

      }

  visit(T)这个操作就是对当前数据进行的处理,preorder_recursive(T->;lchild)就是把当前数据变换为它的左子树,访问右子树的操作可以同样理解了.

  现在回到我们提出的第二个问题:

如何确定转移到哪里继续执行?

关键在于以下三个地方:

a)确定对当前数据的访问顺序,简单一点说就是确定这个递归程序可以转换为哪种方式遍历的树结构;

b)确定这个递归函数转换为递归调用树时的分支是如何划分的,即确定什么是这个递归调用树的"左子树"和"右子树"

c)确定这个递归调用树何时返回,即确定什么结点是这个递归调用树的"叶子结点.

三.两个例子

  好了上面的理论知识已经足够了,下面让我们看看几个例子,结合例子加深我们对问题的认识:

   1)例子一:

f(n)= n+1; (n<2) 

        f[n/2]+f[n/4](n>=2);

      这个例子相对简单一些,递归程序如下:

      int   f_recursive(intn)

      {

             intu1,u2,f;

             if(n<2)

                    f=n+1;

             else{

                    u1=f_recursive((int)(n/2));

                    u2=f_recursive((int)(n/4));

                    f=u1*u2;                                                                        

             }

             returnf;

      }

  下面按照我们上面说的,确定好递归调用树的结构,这一步是最重要的.首先,什么是叶子结点,我们看到当n<2时f=n+1,这就是返回的语句,有人问为什么不是f=u1*u2,这也是一个返回的语句呀?

答案是:

这条语句是在u1=exmp1((int)(n/2))和u2=exmp1((int)(n/4))之后执行的,是这两条语句的父结点.其次,什么是当前结点,由上面的分析,f=u1*u2即是父结点.然后,顺理成章的u1=exmp1((int)(n/2))和u2=exmp1((int)(n/4))就分别是左子树和右子树了.最后,我们可以看到,这个递归函数可以表示成后序遍历的二叉调用树.好了,树的情况分析到这里,

下面来分析一下栈的情况,看看我们要把什么数据保存在栈中,在上面给出的后序遍历的如果这个过程你没非递归程序中我们已经看到了要加入一个标志域,因此在栈中要保存这个标志域;另外,u1,u2和每次调用递归函数时的n/2和n/4参数都要保存,这样就要分别有三个栈分别保存:

标志域,返回量和参数,不过我们可以做一个优化,因为在向上一层返回的时候,参数已经没有用了,而返回量也

只有在向上返回时才用到,因此可以把这两个栈合为一个栈.

如果对于上面的分析你没有明白,建议你根据这个递归函数写出它的递归栈的变化情况以加深理解,再次重申一点:

前期对树结构和栈的分析是最重要的,如果你的程序出错,那么请返回到这一步来再次分析,最好把递归调用树和栈的变化情况都画出来,并且结合一些简单的参数来人工分析你的算法到底出错在哪里.

2)例子二

快速排序算法

递归算法如下:

voidswap(intarray[],intlow,inthigh)

      {

             inttemp;

             temp=array[low];

             array[low]=array[high];

             array[high]=temp;

      }

      int   partition(intarray[],intlow,inthigh)

      {

             int   p;

             p=array[low];

             while(low

                    while(low=p)

                           high--;

                    swap(array,low,high);

                    while(low

                           low++;

                    swap(array,low,high);

             }

             returnlow;

      }

      voidqsort_recursive(intarray[],intlow,inthigh)

      {

             intp;

             if(low

                    p=partition(array,low,high);

                    qsort_recursive(array,low,p-1);

                    qsort_recursive(array,p+1,high);

             }

      }

  需要说明一下快速排序的算法:

partition函数根据数组中的某一个数把数组划分为两个部分,左边的部分均不大于这个数,右边的数均不小于这个数,然后再对左右两边的数组再进行划分.这里我们专注于递归与非递归的转换,partition函数在非递归函数中同样的可以调用(其实partition函数就是对当前结点的访问).

  

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1