一道华为面试题的分析(约瑟夫问题)

/*

据说是华为面试的一道题目:

 

有一个数组a[1000]存放1000(干扰);要求每隔二个数删掉一个数,到末尾时循环至开头继续进行,求最后一个被删掉的数的原始下标位置。

  以个数为例:

        {0,1,2,3,4,5,6,7} (干扰,其实完全没有必要非得从0-(n-1))

  0-->1-->2(删除)-->3-->4-->5(删除)-->6-->7-->0(删除),如此循环直到最后一个数被删除。 

程序就不写1000数组的了,10个元素吧,解决问题即可

*/

 

#include <iostream>

#include <assert.h>

#include <time.h>

using namespace std;

 

int deleted(int a[],const int size,const int cnt)

{

 

assert(a!=NULL&&size>0&&cnt>0);

 

//for循环并非算法必须

for(int i=0;i<size;i++)a[i]=i;

 

int  curCnt=0;

int Deleted;

int DelCnt=0;

int flag=-1;

 

     do

     {   

         for(int i=0;i<size;i++)

         {

              /*

                   在遍历过程中,只要遇到不是flag的数字,计数器+1

                   当计数器=cnt,删除当前数字(置为flag),将下标记录到delete,同时增加已经删除的个数DelCnt,重置计数器curCnt=0;

              */

              if(a[i]!=flag)

              {

                   curCnt++;

                   if(curCnt==cnt)

                   {

                       a[i]=flag;

                       Deleted=i;

                       DelCnt++;

                       curCnt=0;

 

                       if(size==DelCnt)break;

                   }

              }

 

         //以下代码显示当前数组情况

     /*

         for(int i=0;i<size;i++)

              cout<<a[i]<<",";

         cout<<endl;

     */

         }

 

     }while(size!=DelCnt);

 

     return Deleted;

}

 

int main()

{

     int b[10]={0,1,2,3,4,5,6,7,8,9};

     cout<<"最后一个删除的数组元素的下标:"<<deleted(b,10,3)<<endl;

 

     system("pause");

 

}

 

/*

运行结果:

 

0,1,2,3,4,5,6,7,8,9,

0,1,2,3,4,5,6,7,8,9,

0,1,-1,3,4,5,6,7,8,9,

0,1,-1,3,4,5,6,7,8,9,

0,1,-1,3,4,5,6,7,8,9,

0,1,-1,3,4,-1,6,7,8,9,

0,1,-1,3,4,-1,6,7,8,9,

0,1,-1,3,4,-1,6,7,8,9,

0,1,-1,3,4,-1,6,7,-1,9,

0,1,-1,3,4,-1,6,7,-1,9,

0,1,-1,3,4,-1,6,7,-1,9,

0,-1,-1,3,4,-1,6,7,-1,9,

0,-1,-1,3,4,-1,6,7,-1,9,

0,-1,-1,3,4,-1,6,7,-1,9,

0,-1,-1,3,4,-1,6,7,-1,9,

0,-1,-1,3,4,-1,6,7,-1,9,

0,-1,-1,3,4,-1,-1,7,-1,9,

0,-1,-1,3,4,-1,-1,7,-1,9,

0,-1,-1,3,4,-1,-1,7,-1,9,

0,-1,-1,3,4,-1,-1,7,-1,9,

-1,-1,-1,3,4,-1,-1,7,-1,9,

-1,-1,-1,3,4,-1,-1,7,-1,9,

-1,-1,-1,3,4,-1,-1,7,-1,9,

-1,-1,-1,3,4,-1,-1,7,-1,9,

-1,-1,-1,3,4,-1,-1,7,-1,9,

-1,-1,-1,3,4,-1,-1,7,-1,9,

-1,-1,-1,3,4,-1,-1,7,-1,9,

-1,-1,-1,3,4,-1,-1,-1,-1,9,

-1,-1,-1,3,4,-1,-1,-1,-1,9,

-1,-1,-1,3,4,-1,-1,-1,-1,9,

-1,-1,-1,3,4,-1,-1,-1,-1,9,

-1,-1,-1,3,4,-1,-1,-1,-1,9,

-1,-1,-1,3,4,-1,-1,-1,-1,9,

-1,-1,-1,3,4,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,9,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

-1,-1,-1,3,-1,-1,-1,-1,-1,-1,

最后一个删除的数组元素的下标:3

请按任意键继续. . .

*/

/*

下面采用一种新方法来实现

solve Joseph’s problem,常用的方法是使用链表,但在这个题目中只给定了一个数组,虽然没有说用链表不行,不过能充分利用当前的条件解决问题也是一种好品质,话说回来,谁说链表一定要离散存储了?连续存储的链表不就是数组吗?如果链表节点只有一个NEXT指针,呵呵,数组,纯数组!

 

这个想法,我是从泰安师专 崔进平 教授在2001年写的一篇《约瑟夫问题的几种算法》中看到的,文中提到了四种方法:

1.  逻辑数组方法(true,false标记)

2.  整形数组方法(1,0标记,报数时采用累加求和)

3.  数组链表方法(这就是下面的程序的思维源泉)

4.  单循环方法

各个方法之间有细微的差别,别的算法我们就不详细介绍了,首先把 崔进平 教授的数组链表方法写的约瑟夫算法列出来(代码有些老了,看不太明白,直接抄过来的,抄写过程没有文字错误.@_@)

 

PROGRAM joseph3(input,ouput);

       CONST max=100;

       VAR a:ARRAY[1..max] OF integer;n,m,I,j,k:integer;

       BEGIN

              write(‘input n,m=);readln(n,m);

              FOR i:=1 TO n-1 DO a[i]:=i+1;

              a[n]:=1;k:=n;

              FOR i:=1 TO n DO

       BEGIN

              FOR j:=I TO m-1 DO k:=a[k];

              write(a[k]:5);a[k]:=a[a[k]];

              IF I mon I0=0 THEN writeln

       END

END

 

回到前面的华为笔试题目,结合 崔 教授的思想,自己用C++语言实现了一下,也方便后来人.

同 时把崔 教授喜欢的i,j,k之类的统统除去了

关于I,j,k这样的变量,自己在写程序的时候最好不要跟风,如果不是一个很简单的变量(比如简单的循环过程中的控制变量),变量的命名应该见名知意,否则难以理解,给其他读代码的人造成障碍。

 

*/

 

/*

//================================================================

//下面是另一个函数了,同时规范一下代码,不过比微软的代码还差的很远,不仅仅是规范上@_@

 

/*

函数名称:     joseph

 

入口参数:

参数名称      参数类型      意义

aryProc       int []        待处理的整形数组

intSize       const int     待处理数组的长度

intCnt        const int     步长...

 

 

返回参数;

参数类型      意义

int           最后删除的元素的下标

*/

int joseph(int aryProc[],const int intSize,const int intCnt)

{

     #define FLAG -1

     assert(aryProc&&intSize>0&&intCnt>0);

 

     for(int i=0;i<intSize-1;i++)aryProc[i]=i+1;

     aryProc[intSize-1]=0;

 

     /*

 

     声明四个变量

     intRemain     当前剩下的有效节点

     intDeleted    最后删除的一个节点的下标

     intCurrent    指向当前元素的指针

     intCurCnt     计数器

    

     */

     int intRemain,intDeleted,intCurrent,intCurCnt;

 

     intRemain=intSize;

     intDeleted=-1;

     intCurrent=0;

     intCurCnt=0;

    

     do

     {

         //当前节点有效的标准是:不为-1(FLAG)

         if(aryProc[intCurrent]!=FLAG)

         {

              //又发现一个活着的.嘻嘻

              intCurCnt++;

              if(intCnt-intCurCnt==1)

              {

                   //删除后一个,主要是将当前节点的向量指向下一个节点的下一个节点

                   intDeleted=aryProc[intCurrent];

                   aryProc[intCurrent]=aryProc[intDeleted];

                   aryProc[intDeleted]=-1;

                   intRemain--;

                   intCurCnt=0;

              }

         }

         intCurrent=aryProc[intCurrent];//当删除最后一个元素后,intCurrent变为-1,幸好后面已经不再使用该值来访问数组.

 

     }while(intRemain!=0);

 

     return intDeleted;

}

 

修改一下main函数,做个测试,看看我自己一开始写的代码和后来的函数

 

int main()

{

     int a[10000];

     int b[1];

     int c[3];

     int d[10];

     int e[3];

     int f[5];

     int g[8];

 

     clock_t start,finish;

 

     cout<<"A: 使用flag作为删除标志,遍历数组进行删除操作"<<endl;

     cout<<"B: 使用向量数组,把数组作为链表来使用,仿照链表进行删除操作"<<endl<<endl;

 

     cout<<"正确性验证(可以自己数一下)"<<endl;

 

     cout<<"A: b[1], 步长为最后一个删除的数组元素的下标:"<<deleted(b,1,3)<<endl;

     cout<<"B: b[1], 步长为最后一个删除的数组元素的下标:"<<joseph(b,1,3)<<endl<<endl;

 

     cout<<"A: c[3], 步长为最后一个删除的数组元素的下标:"<<deleted(c,3,3)<<endl;

     cout<<"B: c[3], 步长为最后一个删除的数组元素的下标:"<<joseph(c,3,3)<<endl<<endl;

 

     cout<<"A: d[10],步长为最后一个删除的数组元素的下标:"<<deleted(d,10,3)<<endl;

     cout<<"B: d[10],步长为最后一个删除的数组元素的下标:"<<joseph(d,10,3)<<endl<<endl;

 

     cout<<"A: e[3], 步长为最后一个删除的数组元素的下标:"<<deleted(e,3,4)<<endl;

     cout<<"B: e[3], 步长为最后一个删除的数组元素的下标:"<<joseph(e,3,4)<<endl<<endl;

 

     cout<<"A: f[5], 步长为最后一个删除的数组元素的下标:"<<deleted(f,5,4)<<endl;

     cout<<"B: f[5], 步长为最后一个删除的数组元素的下标:"<<joseph(f,5,4)<<endl<<endl;

 

     cout<<"A: g[8], 步长为最后一个删除的数组元素的下标:"<<deleted(g,8,4)<<endl;

     cout<<"B: g[8], 步长为最后一个删除的数组元素的下标:"<<joseph(g,8,4)<<endl<<endl<<endl;

 

 

     cout<<"效率验证"<<endl;

     start=clock();

     for(int i=0;i<1000;i++)deleted(a,10000,3);

     finish=clock();

     cout<<"A: a[10000] 步长为约瑟夫问题的次解决时间:"<<(double)(finish-start)/1000<<endl;

 

     start=clock();

     for(int i=0;i<1000;i++)joseph(a,10000,3);

     finish=clock();

     cout<<"B: a[10000] 步长为约瑟夫问题的次解决时间:"<<(double)(finish-start)/1000<<endl<<endl;

    

     start=clock();

     for(int i=0;i<10000;i++)deleted(a,10000,3);

     finish=clock();

     cout<<"A: a[10000] 步长为约瑟夫问题的次解决时间:"<<(double)(finish-start)/1000<<endl;

 

     start=clock();

     for(int i=0;i<10000;i++)joseph(a,10000,3);

     finish=clock();

     cout<<"B: a[10000] 步长为约瑟夫问题的次解决时间:"<<(double)(finish-start)/1000<<endl<<endl;

 

         start=clock();

     for(int i=0;i<1000;i++)deleted(a,10000,5);

     finish=clock();

     cout<<"A: a[10000] 步长为约瑟夫问题的次解决时间:"<<(double)(finish-start)/1000<<endl;

 

     start=clock();

     for(int i=0;i<1000;i++)joseph(a,10000,5);

     finish=clock();

     cout<<"B: a[10000] 步长为约瑟夫问题的次解决时间:"<<(double)(finish-start)/1000<<endl<<endl;

    

     start=clock();

     for(int i=0;i<1000;i++)deleted(a,10000,100);

     finish=clock();

     cout<<"A: a[10000] 步长为约瑟夫问题的次解决时间:"<<(double)(finish-start)/1000<<endl;

 

     start=clock();

     for(int i=0;i<1000;i++)joseph(a,10000,100);

     finish=clock();

     cout<<"B: a[10000] 步长为约瑟夫问题的次解决时间:"<<(double)(finish-start)/1000<<endl<<endl;

    

     system("pause");

         

     return 0;

}

 

/*

运行结果如下:

A:      使用flag作为删除标志,遍历数组进行删除操作

B:      使用向量数组,把数组作为链表来使用,仿照链表进行删除操作

 

正确性验证(可以自己数一下)

A:      b[1], 步长为3 最后一个删除的数组元素的下标:0

B:      b[1], 步长为3 最后一个删除的数组元素的下标:0

 

A:      c[3], 步长为3 最后一个删除的数组元素的下标:1

B:      c[3], 步长为3 最后一个删除的数组元素的下标:1

 

A:      d[10],步长为3 最后一个删除的数组元素的下标:3

B:      d[10],步长为3 最后一个删除的数组元素的下标:3

 

A:      e[3], 步长为4 最后一个删除的数组元素的下标:1

B:      e[3], 步长为4 最后一个删除的数组元素的下标:1

 

A:      f[5], 步长为4 最后一个删除的数组元素的下标:0

B:      f[5], 步长为4 最后一个删除的数组元素的下标:0

 

A:      g[8], 步长为4 最后一个删除的数组元素的下标:5

B:      g[8], 步长为4 最后一个删除的数组元素的下标:5

 

 

效率验证

A:      a[10000] 步长为3 约瑟夫问题的1000次解决时间:1.219

B:      a[10000] 步长为3 约瑟夫问题的1000次解决时间:0.343

 

A:      a[10000] 步长为3 约瑟夫问题的10000次解决时间:12.125

B:      a[10000] 步长为3 约瑟夫问题的10000次解决时间:3.422

 

A:      a[10000] 步长为5 约瑟夫问题的1000次解决时间:2.375

B:      a[10000] 步长为5 约瑟夫问题的1000次解决时间:0.516

 

A:      a[10000] 步长为100 约瑟夫问题的1000次解决时间:48.719

B:      a[10000] 步长为100 约瑟夫问题的1000次解决时间:7.859

 

请按任意键继续. . .

 

*/

 

@_@   @_@  @_@   !!!!!!!

继续努力

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值