C++怎么实现约瑟夫环问题_C++数学递推与模拟【经典】

2次阅读

std::list 模拟约瑟夫环最直观,其 erase 和迭代器自增天然适配“跳 m-1 个、删第 m 个”,无需手动处理下标越界,初始化可用 for (int i = 1; i

C++怎么实现约瑟夫环问题_C++数学递推与模拟【经典】

std::list 模拟删除过程最直观

约瑟夫环本质是循环链表删节点,std::listerase 和迭代器自增天然适配“跳 m-1 个、删第 m 个”的逻辑,不用手动管理下标越界。

  • 初始化时用 for (int i = 1; i ,别从 0 开始——题干通常编号从 1 起
  • 迭代器走到末尾后要重置:if (it == lst.end()) it = lst.begin();,漏这句会崩溃
  • 删完一个元素后,erase 返回下一个有效迭代器,但你得先走 m-1 步再删,所以建议删前用 std::next(it, m-1) 计算目标位置,再处理循环绕回

josephus(n, m) 递推公式怎么写对

递推式 f(1)=0f(k)=(f(k-1)+m)%k 是基于 0-index 的结果,直接套用会比题目要求的编号小 1。

  • 如果题目说“1~n 编号,报到 m 出列”,最终答案必须加 1:return f(n) + 1
  • m 可能大于当前人数 k,但取模已隐含处理,无需提前 m %= k——反而错,因为递推依赖原始 m
  • 递推适合大 n(比如 1e6),但要注意 int 溢出:若 m 很大且 n 大,f(k-1)+m 可能超 INT_MAX,改用 long long

模拟法 vs 递推法:什么时候选哪个

两者时间复杂度差一个数量级,但不是无脑选快的。

  • 需要输出完整出列顺序?只能模拟,递推只给最后幸存者
  • n 是 1e5 但 m 是 2?用递推,O(n) 稳定;若 m 接近 n,模拟中 std::list::erase 平均仍是 O(1),总 O(n),差别不大
  • 面试里写递推容易被追问“为什么是 (f(k-1)+m)%k”,得能讲清:上一轮幸存者在新编号体系里的偏移量,加上步长,再映射回本轮编号

边界情况一碰就错

输入 n=1m=1 时,所有实现都可能崩或返回错值。

立即学习C++免费学习笔记(深入)”;

  • n == 1:无论 m 是多少,答案就是 1,递推里 f(1)=0 后加 1 刚好,但模拟代码若没单独判断,list 遍历时可能空跑
  • m == 1:每轮删第一个,结果一定是 n,递推公式此时退化为 f(k) = (f(k-1)+1)%k,手算几轮就能验证;但有人写成 (f(k-1)+m-1)%k 就全错了
  • 测试一定要覆盖 n=1, m=1n=5, m=2(经典 3)、n=7, m=3 这三组,答案分别是 1、3、4

递推的数学形式看着干净,但下标偏移和初始条件错一位,结果就全偏;模拟看似啰嗦,反而容错高。实际写的时候,先跑通小样例,再换大数测性能。

text=ZqhQzanResources