/*
据说是华为面试的一道题目:
有一个数组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
请按任意键继续. . .
*/
@_@ @_@ @_@ !!!!!!!
继续努力