zoukankan      html  css  js  c++  java
  • 约瑟夫环的C语言数组实现

     

    约瑟夫环问题的具体描述是:设有编号为1,2,……,n的n个(n>0)个人围成一个圈,从第1个人开始报数,报到m时停止报数,报m的人出圈,才从他的下一个人起重新报数,报到m时停止报数,报m的出圈,……,如此下去,知道剩余1个人为止。当任意给定n和m后,设计算法求n个人出圈的次序。

    一开始看到这这个题目就是觉得这是一个环形的,想到了用链表和用指针,然后看题目的要求是使用数组实现。就先暂时放弃用链表的办法,用数组实现之后再用链表来实现。

    一开始的思路是:

    1、建立一个长度为n的数组。

    2、取出位置编号为(i+1)*m的数显示,将位置编号为(i+1)*m的数置0;

    3、第一轮取完之后的数再组成一个新的数组,用2的办法再取;

    4、重复3的操作,最后剩下m-1个数字。

    经过实际编程调试发现这个办法很难实现,效率特别低,容易出错,仅仅是第一轮取完数字再组成一个新的数组就很费劲了,如果数字比较少的话可以直接算出来,如果数据量大,就很麻烦。

    后来,就反思一开始的思路其实就是模拟了一个“环“,通过好多数组来拼接一个环。这样就很麻烦,逻辑上也很混乱,最后也是看了其他人的代码,觉得用指针的思想到最后一个数的时候跳到第一个就可以了。最早的时候我也有想到过用指针的思想去解决,但是不会用,主要原因是因为我不知道有这样i = (i + m -1) % n一个公式,公式中i就是出圈的那个数字,知道了这个公式之后就是每次需要把出圈的数字删除了就可以,继续用这个公式找到下一个需要出圈的数字。最后找到只剩一个的时候跳出,中间如果找到某一时刻数组的最后一个数字的时候跳到第一个就可以了,这样就形成了一个环。

    根据这个思路完成了下边的这个程序,这个程序是7个数,报3出圈。

    #include <stdio.h>
    
    #include <string.h>
    
     int main()
    
     {
    
         int m = 3;
    
         int n = 7;
    
            int a[7] = {1,2,3,4,5,6,7};
    
            int i;
    
            int j;
    
         for(i = 0; i < n; i++)
    
         {
    
             a[i] = i+1;
    
         }
    
         while (n > 1)
    
         {
    
             i = (i + m - 1) % n;
    
             printf("第一个出圈的是%d
    ",a[i]);
    
             for(j = i+1; j < n; j++)
    
             {
    
                 a[j-1] = a[j];
    
             }
    
             n--;
    
             if(i == n)
    
             {
    
                 i = 0;
    
             }
    
         }
    
         printf("最后剩下的是%d
    ", a[i]);
    
         return 0;
    
     }

    上边这段代码在调试的过程中出了一个非常低级的错误,编译完成后开始运行的时候发现,程序确实在运行但是什么都不显示,就开始一点一点的调试,发现问题出在了循环部分,发现循环内部是没有问题的,按照逻辑分析是可以跳出循环的,而且循环中是有显示的,如果不能跳出循环肯定会连续的显示,后来就意识到是没有进入到循环中,又看了很多遍程序才发现是在while(n > 1)这句话后边加了“;”号,这样的低级错误确实不应该,编程太少,所以找到错误花了比较长的时间,还是需要多自主编程,积累经验。

    根据上边的程序我也进行了一些简单的修改,能够实现定制,也就是你可以自己设置输入的m,n值,代码如下。

    #include <stdio.h>
    
    #include <string.h>
    
    #define N 100
    
    int main()
    
      {
    
          int m;
    
          int n;
    
             printf("请输入总人数n 
    ");
    
             scanf("%d",&n);
    
             printf("请输入报的数m 
    ");
    
             scanf("%d",&m);
    
          int a[N] = {0};
    
          int i;
    
          int j;
    
             int k = 0;
    
             for(i = 0; i < n; i++)
    
          {
    
              a[i] = i+1;
    
          }
    
          while (n > 1)
    
          {
    
              i = (i + m - 1) % n;
    
                    k++;
    
                    printf("第%d个出圈的是%d
    ",k,a[i]);
    
              for(j = i+1; j < n; j++)
    
              {
    
                  a[j-1] = a[j];
    
              }
    
              n--;
    
              if(i == n)
    
                    {
    
                        i = 0;
    
              }
    
          }
    
          printf("最后剩下的时%d
    ", a[i]);
    
          return 0;
    
      }

    有了第一个代码,第二个随机输入就好实现了,但是在编程过程中还是出现了点问题,因为人数是随机输入的,那么n的值就没办法确定了,定义数组的时候a[n]这种方式是错误的,因此需要定义一个较大的数组,数组的后半段在程序中不使用就可以了。另外scanf函数在使用的时候要记得加”&”,不然会出现段错误,如果调试过程中出现了段错误,可以看看代码中是不是忘记加“&”符号了。

    约瑟夫环也可以用链表的方式来实现,接下来会再用链表的方式来实现以下,后边的博客会实现。

    这是新年之后的第一篇博客,新年之后能够快速的进入学习状态是一个好的开端。新一年,同时又是编程学习的新的重要的阶段,开始自己写程序,摆脱抄程序的过程。新年中也反思了去年的学习,专注力不够是一个很大的问题,原因在于上进心不足,眼界不够,看不到未来让我觉得迷茫,懈怠。今年是我人生的一个转折点,提高专注力,用好每天的学习时间,把最能专心学习的时间用在学习上,加强自律。

  • 相关阅读:
    面试官:请说一下对象锁和类锁的区别
    手撕 JVM 垃圾收集日志
    JVM 问题排查和性能优化常用的 JDK 工具
    JVM 中你不得不知的一些参数
    微信授权就是这个原理,Spring Cloud OAuth2 授权码模式
    基准测试了 ArrayList 和 LinkedList ,发现我们一直用 ArrayList 也是没什么问题的
    Spring Cloud OAuth2 实现用户认证及单点登录
    后端开发有必要学习前端吗,如何入门呢
    无意间做了个 web 版的 JVM 监控端(前后端分离 React+Spring Boot)
    走进AngularJs(一)angular基本概念的认识与实战
  • 原文地址:https://www.cnblogs.com/Mr--Yang/p/6391554.html
Copyright © 2011-2022 走看看