C语言指针-报数筛选问题

397 阅读2分钟

本文已参加[新人创作礼]活动,一起开启掘金创作之路。


有 n 个人围成一圈,顺序排号。从第一个人开始报数(从 1 报到 3),凡是报到 3 的人退出圈子,问最后留下的是原来第几号的那位。

就是逢3筛出来,然后按照出局的人数还剩一人结束循环

这里我使用二种方法:

1. 数组

#include <stdio.h>
#define N 3                                   //人数
int main() 
{
	int a[N]={0};              //全部初始化为0,每人都是0

	int i=0,t;
	int count=0;
	int leave=N;          //剩下的人数

        printf("原来是:\n");
    	for(t=0;t<N;t++) printf("%d  ",a[t]);       //原来是
        printf("\n");

     printf("下面是循环过程:\n");
	while (leave!=1){  //一直循环到最后留下了一人
		if(a[i]==0) count++;    //如果没有淘汰 count++
		if(count%3==0) {                 //逢3筛出来
			a[i]=1;
			leave--;
			for(t=0;t<N;t++) printf("%d  ",a[t]);       //输出筛选过程 
			printf("\n");
		}

	    i=(i+1)%N;         //跳到值为0的下一个人
		while(a[i]!=0){    //跳过上次已经去掉的序号3的那个,直接到下一个人
			i=(i+1)%N;      
		}
		
	}
	printf("剩下的是原来的第%d号\n",i+1);
	return 0;
}

可能看着不太理解,你带入一下,

图片.png

利用这个例子:

原来是 0 0 0 3个人
开始

  1. i=0 leave=3 a[i]==0 表示不是出局的人 count++ (1)
    i=(i+1)%N; i=1 下一个人

  2. i=1 leave=3 a[i]==0 表示不是出局的人 count++ (2)
    i=(i+1)%N; i=2 下一个人

  3. i=2 leave=3 a[i]==0 表示不是出局的人 count++ (3) 此时if(count%3==0) {
    筛3出来,a[2]=1 leave=2,输出第一次筛选的过程 0 0 1
    i=(i+1)%N; i变为0

  4. i=0 leave=2 a[i]==0 表示不是出局的人 count++ (4)
    i=(i+1)%N; i=1 下一个人

  5. i=1 leave=2 a[i]==0 表示不是出局的人 count++ (5)
    i=(i+1)%N; i=2下一个人
    这时候
    while(a[i]!=0){ //跳过上次已经去掉的序号3的那个,直接到下一个人i=(i+1)%N; }
    a[2]!=0 所以i=(i+1)%N; 跳过上次被去掉的人 直接下一个 i=0

  6. i=0 leave=2 a[i]==0 表示不是出局的人 count++ (6) 此时if(count%3==0) {
    筛3出来,a[0]=1 leave=1,输出第一次筛选的过程 1 0 1
    i=(i+1)%N; i=1下一个人
    这时候while (leave!=1)
    leave=1 所以循环结束

  7. printf("剩下的是原来的第%d号\n",i+1);

2. 指针数组

#include <stdio.h>
#define N 3                                    //人数
int main()
  {
      int a[N] = {0}, out = 0, num = 0, *flag;
      flag = a;           //指向首地址


      while (1){                                  //循环报数
          if(*flag == 0){                            //如果没有淘汰 0表示没出局
              if (out == (N - 1)) break;          //如果仅剩一人,结束


              num++;                              //报数 +1+1
              num %= 3;                           //最大为3,到了3就从0开始 逢3筛出来
              if(num == 0) //为0(即3)出局
              {                      
                  *flag = 1;  //出局,*flag变为1标志 1表示出局
                  out++;      //出局人+1
              }
          }


          flag++;   //下一个人


          if (flag == a + N)  //如果flag的地址等于(a的首地址+N)的地址        
              flag = a;      //重新从首地址开始,第二次筛选           
      }


      printf("最后剩余者的编号是: %ld\n", flag-a+1); //打印那个最后值的地址
      
  }

这个跟上面的思路差不多

  1. 开始 0 0 0 out出局0 num=0
  2. flag=a,指向首地址
  3. *flag==0 表示没出局 num++ (1)
  4. flag++ 下一个人地址 a+1 num++(2)
  5. flag++ 下一个人地址 a+2 num++(3)
  6. num %= 3; if(num == 0) //为0(即3)出局
  7. *flag = 1 out++(1)
  8. 这时候为 0 0 1
  9. flag++ 下一个人地址,但是等于a+N 超过数组长度
  10. 这时候flag地址超过a长度 为a+N
  11. flag=a 重新首地址开始
  12. 第二轮
  13. *flag==0 表示没出局 num++ (4)
  14. flag++ 下一个人地址 a+1
  15. *flag==0 表示没出局 num++ (5)
  16. a+2 *flag=1 if(*flag == 0){ 直接不满足 下一个flag++ a+3
  17. 这时候flag地址超过a长度 为a+N flag=a 重新首地址开始
  18. *flag==0 表示没出局 num++ (6)
  19. num %= 3; if(num == 0) //为0(即3)出局
  20. *flag = 1 out++(2)
  21. if (out == (N - 1)) break;
  22. 结束
  23. 这时候数组为 1 0 1
  24. 输出
  25. printf("最后剩余者的编号是: %ld\n", flag-a+1); //打印那个最后值的地址
  26. 打印的是地址
  27. 可以使用%p打印但是是十六进制,这里使用flag-a首地址 再+1 来表示flag此时的地址
  28. 这样打印出来就是int型

图片.png