【算法竞赛】实现约瑟夫问题的四种方法(附手绘图详解)

简介: 【算法竞赛】实现约瑟夫问题的四种方法(附手绘图详解)

题目描述


n 个人围成一圈,从第一个人开始报数,数到 m 的人出列,再由下一个人重新从 1开始报数,数到 m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。


输入格式

输入两个整数 n,m。


输出格式

输出一行 n 个整数,按顺序输出每个出圈人的编号。


输入输出样例

输入


10 3



输出


3 6 9 2 7 1 8 5 10 4



说明/提示

1≤m,n≤100


1.动态单向链表实现的方法


动态链表需要临时分配链表节点,使用完毕后需要释放链表节点,动态链表的优点是能及时释放空间,不用使用多余的内存,缺点是需要管理空间,比较容易出错。


#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
struct node
{
  int data;
  node* next;//单向链表,一个next指针
};
int main()
{
  int m, n;
  scanf("%d%d", &n, &m);
  node* head, * p, * now, * prev;
  head = new node; head->data = 1; head->next = NULL;//分配第一个节点,数据置为1
  now = head;
  for (int i = 2; i <= n; i++)
  {
  p = new node; p->data = i; p->next = NULL;//把申请的新节点链接到前面的链表上
  now->next = p;
  now = p;
  }
  now->next = head;
  //上面是建立链表。
  now = head, prev = head;
  while ((n--) > 1)
  {
  for (int i = 1; i < m; i++)//数到m为止
  {
    prev = now;//类似于单链表的元素删除,记录前一个位置,用于下面跳过第m个节点
    now = now->next;
  }
  printf(" %d ", now->data);
  prev->next = now->next;
  delete now;
  now = prev->next;
  }
  printf(" %d", now->data);//最后一个节点的打印
  delete now;//释放掉最后一个节点
  return 0;
}


为了方便大家理解思路,我做出了第一次while循环的图示 ,为了简便,节点就设置成了4个,m的值就设置为3。


5fb154712653a62023619766c195ee4f_c4852cde46e04deea1acf240afae2583.jpeg


2.用结构体数组实现单向静态链表实现的方法


静态链表较动态链表省去了动态分配和释放储存空间的麻烦。可以加快编码的速度。


#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
const int N = 100;//定义静态链表的空间大小
struct node
{
  int id, nextid;
}nodes[N];
int main()
{
  int n, m;
  scanf("%d%d", &n, &m);
  nodes[0].nextid = 1;
  for (int i = 1; i <= n; i++)
  {
  nodes[i].id = i, nodes[i].nextid = i + 1;
  }
  nodes[n].nextid = 1;
  int now = 1, prev = 1;
  while ((n--) > 1)
  {
  for (int i = 1; i < m; i++)
  {
    prev = now;
    now = nodes[now].nextid;
  }
  printf(" %d ", nodes[now].id);
  now = nodes[prev].nextid;
  }
  printf(" %d", nodes[now].nextid);
  return 0;
}


同样是画了个简图方便理解(不要嫌弃我字丑🤗🤗🤗)


f1885a9e5a9a65547195c8797e7a6391_9091ac939f664275971d751b2827a167.jpeg


3.用结构体数组实现双向静态链表实现的方法


#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
const int N = 100;
struct node
{
  int id;
  int preid, nextid;//前后节点
}nodes[N];
int main()
{
  int n, m;
  scanf("%d%d", &n, &m);
  nodes[0].nextid = 1;
  for (int i = 1; i <= n; i++)
  {
  nodes[i].id = i;
  nodes[i].preid = i - 1;
  nodes[i].nextid = i + 1;
  }
  nodes[n].nextid = 1;//循环链表,尾指向头
  nodes[1].preid = n;//循环链表,头指向尾
  int now = 1;//从第一个节点开始
  while ((n--) > 1)
  {
  for (int i = 1; i < m; i++)
  {
    now = nodes[now].nextid;
  }
  printf(" %d ", nodes[now].id);
  int prev = nodes[now].preid, next = nodes[now].nextid;
  nodes[prev].nextid = nodes[now].nextid;
  nodes[next].preid = nodes[now].preid;
  now = next;//新的开始
  }
  printf(" %d", nodes[now].nextid);
  return 0;
}


同样的,简图方便大家理解。


e76e6081b3f8799ef670ce22a5412611_e2fbdc74c11b454d98184d7a74df47b9.png


4.一维数组实现单向循环链表


这是最简单的实现方法。定义一个一维数组,数组的第i个节点的i就是节点的值。


#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
int nodes[150];
int main()
{
  int n, m;
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n - 1; i++)
  {
  nodes[i] = i + 1;//nodes[i]的值就是下一个节点
  }
  nodes[n] = 1;
  int now = 1, prev = 1;
  while ((n--) > 1)
  {
  for (int i = 1; i < m; i++)
  {
    prev = now; now = nodes[now];
  }
  printf(" %d ", now);
  nodes[prev] = nodes[now];//跳过now节点
  now = nodes[prev];//新的now节点
  }
  printf(" %d", now);
  return 0;
}


这个就不画图了。


现在我是在自学c++,还不是很熟练。


总结

 感谢观看,本文到这里就结束了,如果觉得有帮助,请给文章点个赞吧,让更多的人看到。🌹 🌹 🌹

cad3f4971ac28288f5f73c67c1f7b77b_7673ea0a00c3431893891e0c2913a10e.jpeg

 


相关文章
|
1月前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
41 3
|
5月前
|
数据采集 机器学习/深度学习 算法
机器学习方法之决策树算法
决策树算法是一种常用的机器学习方法,可以应用于分类和回归任务。通过递归地将数据集划分为更小的子集,从而形成一棵树状的结构模型。每个内部节点代表一个特征的判断,每个分支代表这个特征的某个取值或范围,每个叶节点则表示预测结果。
172 1
|
1月前
|
算法 索引
HashMap扩容时的rehash方法中(e.hash & oldCap) == 0算法推导
HashMap在扩容时,会创建一个新数组,并将旧数组中的数据迁移过去。通过(e.hash & oldCap)是否等于0,数据被巧妙地分为两类:一类保持原有索引位置,另一类索引位置增加旧数组长度。此过程确保了数据均匀分布,提高了查询效率。
38 2
|
1月前
|
搜索推荐 Shell
解析排序算法:十大排序方法的工作原理与性能比较
解析排序算法:十大排序方法的工作原理与性能比较
51 9
|
1月前
|
存储 算法 Java
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
前缀(波兰)表达式、中缀表达式和后缀(逆波兰)表达式的基本概念、计算机求值方法,以及如何将中缀表达式转换为后缀表达式,并提供了相应的Java代码实现和测试结果。
53 0
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
|
3月前
|
JavaScript 算法 前端开发
JS算法必备之String常用操作方法
这篇文章详细介绍了JavaScript中字符串的基本操作,包括创建字符串、访问特定字符、字符串的拼接、位置查找、大小写转换、模式匹配、以及字符串的迭代和格式化等方法。
JS算法必备之String常用操作方法
|
3月前
|
JavaScript 算法 前端开发
JS算法必备之Array常用操作方法
这篇文章详细介绍了JavaScript中数组的创建、检测、转换、排序、操作方法以及迭代方法等,提供了数组操作的全面指南。
JS算法必备之Array常用操作方法
|
6月前
|
存储 算法
【软件设计师】常见的算法设计方法——递推法
【软件设计师】常见的算法设计方法——递推法
|
3月前
|
搜索推荐 算法 Java
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
该博客文章通过UML类图和Java源码示例,展示了如何使用适配器模式将QuickSort类和BinarySearch类的排序和查找功能适配到DataOperation接口中,实现算法的解耦和复用。
40 1
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
|
3月前
|
算法 定位技术 vr&ar
一文了解PnP算法,python opencv中的cv2.solvePnP()的使用,以及使用cv2.sovlePnP()方法标定相机和2D激光雷达
一文了解PnP算法,python opencv中的cv2.solvePnP()的使用,以及使用cv2.sovlePnP()方法标定相机和2D激光雷达
534 0
一文了解PnP算法,python opencv中的cv2.solvePnP()的使用,以及使用cv2.sovlePnP()方法标定相机和2D激光雷达