如何在约瑟夫环问题中获取剩余元素的原列表索引?
解决约瑟夫环问题中保留原始索引的方案
你的核心问题在于:当前循环链表只存储了元素值,没有保留原始数组的索引信息。每次删除元素后,剩余元素的索引是基于当前链表的,和原始数组的索引对应关系已经被打乱,所以靠加减步长的方式根本没法推导出原始索引。
下面给你两个可行的解决思路:
思路1:修改链表节点,同时存储值和原始索引
这是最直接的方案——让链表的每个节点同时保存元素的原始索引和值,比如用std::pair<int, T>作为链表的存储类型。这样无论删除多少元素,最后剩下的节点里直接就有原始索引。
修改后的代码示例:
#include "circular_linked_list.h" enum class direction { clockwise, counterclockwise }; template<typename T> std::pair<int, T> Josephus_problem(CircularLinkedList<std::pair<int, T>> list, direction dir, unsigned step) { if (list.getLength() == 0) { throw std::invalid_argument("List is empty"); } int currentIndex = 0; int currentSize = list.getLength(); while (currentSize > 1) { if (dir == direction::counterclockwise) { // 逆时针计算要删除的索引,处理负数取模 currentIndex = (currentIndex - step + 1) % currentSize; if (currentIndex < 0) { currentIndex += currentSize; } } else { // 顺时针计算要删除的索引 currentIndex = (currentIndex + step - 1) % currentSize; } list.removeAt(currentIndex); currentSize = list.getLength(); // 删除后,下一轮的currentIndex不需要额外调整,因为下一次计算会基于新的链表长度 } // 最后剩下的节点直接包含原始索引和值 auto& survivor = list[0]; return {survivor.first, survivor.second}; }
使用时,初始化链表要把原始索引和值绑定:
// 示例:原始数组为 {5, 8, 12, 15} int arr[] = {5, 8, 12, 15}; CircularLinkedList<std::pair<int, int>> list; for (int i = 0; i < 4; ++i) { list.insertEnd({i, arr[i]}); }
思路2:用约瑟夫环数学公式直接计算原始索引
如果不想修改链表结构,可以跳过模拟过程,直接用递推公式计算最后存活元素的原始索引,再去原数组取值。
顺时针递推公式
设f(n)表示n个元素时,最后存活元素的原始索引:
- 当n=1时,
f(1)=0 - 当n>1时,
f(n) = (f(n-1) + step) % n
逆时针递推公式
逆时针相当于反向计数,递推公式调整为:
- 当n=1时,
f(1)=0 - 当n>1时,
f(n) = (f(n-1) - step + 1) % n,结果为负时加上n确保非负
这种方法不需要循环链表模拟,直接计算出原始索引后,再取对应的值即可,效率更高。
为什么你原来的finalIndex逻辑无效?
你尝试用finalIndex累加/减步长的方式追踪原始索引,但每次删除元素后,剩余元素的相对索引都会变化——比如删除索引1的元素后,原来的索引2会变成新的索引1,这种情况下单纯的加减步长无法对应到原始数组的位置,自然得不到正确的原始索引。
内容的提问来源于stack exchange,提问作者beetard
相关产品推荐
相关产品推荐

