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

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

题目描述


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

 


相关文章
|
29天前
|
机器学习/深度学习 算法 数据挖掘
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构。本文介绍了K-means算法的基本原理,包括初始化、数据点分配与簇中心更新等步骤,以及如何在Python中实现该算法,最后讨论了其优缺点及应用场景。
97 4
|
2月前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
56 3
|
27天前
|
存储 算法 安全
SnowflakeIdGenerator-雪花算法id生成方法
SnowflakeIdGenerator-雪花算法id生成方法
23 1
|
6月前
|
数据采集 机器学习/深度学习 算法
机器学习方法之决策树算法
决策树算法是一种常用的机器学习方法,可以应用于分类和回归任务。通过递归地将数据集划分为更小的子集,从而形成一棵树状的结构模型。每个内部节点代表一个特征的判断,每个分支代表这个特征的某个取值或范围,每个叶节点则表示预测结果。
185 1
|
1月前
|
JSON 算法 数据挖掘
基于图论算法有向图PageRank与无向图Louvain算法构建指令的方式方法 用于支撑qwen agent中的统计相关组件
利用图序列进行数据解读,主要包括节点序列分析、边序列分析以及结合节点和边序列的综合分析。节点序列分析涉及节点度分析(如入度、出度、度中心性)、节点属性分析(如品牌、价格等属性的分布与聚类)、节点标签分析(如不同标签的分布及标签间的关联)。边序列分析则关注边的权重分析(如关联强度)、边的类型分析(如管理、协作等关系)及路径分析(如最短路径计算)。结合节点和边序列的分析,如子图挖掘和图的动态分析,可以帮助深入理解图的结构和功能。例如,通过子图挖掘可以发现具有特定结构的子图,而图的动态分析则能揭示图随时间的变化趋势。这些分析方法结合使用,能够从多个角度全面解读图谱数据,为决策提供有力支持。
|
2月前
|
算法 索引
HashMap扩容时的rehash方法中(e.hash & oldCap) == 0算法推导
HashMap在扩容时,会创建一个新数组,并将旧数组中的数据迁移过去。通过(e.hash & oldCap)是否等于0,数据被巧妙地分为两类:一类保持原有索引位置,另一类索引位置增加旧数组长度。此过程确保了数据均匀分布,提高了查询效率。
48 2
|
2月前
|
搜索推荐 Shell
解析排序算法:十大排序方法的工作原理与性能比较
解析排序算法:十大排序方法的工作原理与性能比较
79 9
|
2月前
|
存储 算法 Java
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
前缀(波兰)表达式、中缀表达式和后缀(逆波兰)表达式的基本概念、计算机求值方法,以及如何将中缀表达式转换为后缀表达式,并提供了相应的Java代码实现和测试结果。
134 0
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
|
2月前
|
机器学习/深度学习 人工智能 开发框架
【AI系统】AI 学习方法与算法现状
在人工智能的历史长河中,我们见证了从规则驱动系统到现代机器学习模型的转变。AI的学习方法基于深度神经网络,通过前向传播、反向传播和梯度更新不断优化权重,实现从训练到推理的过程。当前,AI算法如CNN、RNN、GNN和GAN等在各自领域取得突破,推动技术进步的同时也带来了更大的挑战,要求算法工程师与系统设计师紧密合作,共同拓展AI技术的边界。
123 1
|
4月前
|
JavaScript 算法 前端开发
JS算法必备之String常用操作方法
这篇文章详细介绍了JavaScript中字符串的基本操作,包括创建字符串、访问特定字符、字符串的拼接、位置查找、大小写转换、模式匹配、以及字符串的迭代和格式化等方法。
JS算法必备之String常用操作方法