面试题1:约瑟夫环

约瑟夫环故事背景:
著名犹太历史学家 Josephus有过以下的故事:
在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,
39个犹太人决定宁愿死也不要被敌人抓到,
于是决定了一个自杀方式,41个人排成一个圆圈,
由第1个人开始报数,每报数到第3人该人就必须自杀,
然后再由下一个重新报数,直到所有人都自杀身亡为止。
然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,
他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

问题:怎样确定16和31的位置

解题思路:

从开始位置遍历,当指针到报数为三的位置上时,将第三个位置的节点删除,然后继续循环删除,直到当前节点(指针指向的节点)的后一个节点的next是当前节点时,即留下两个元素。

如图所示:

wKioL1aggyfCpxJ8AAAaxYGPcKY290.png

pLinkNode JoseCycle(pList *pHead, int num)
{
 assert(pHead);
 pLinkNode cur = *pHead;
 pLinkNode del = NULL;
 int count = 0;
 while (1)
 {
  count = num;
  if (cur == cur->next->next) //当前节点的后一个节点的next是当前节点时,结束循环
  {
   break;
  } 
   while (--count)    //找到第num个元素
   {
    cur = cur->next;
   }
   del = cur->next;
   cur->data = cur->next->data;
   cur->next = cur->next->next;
   free(del); //删除第num个元素
   del == NULL;
 }
 *pHead = cur;
 return cur;
}

测试函数:

void Test13() // 测试约瑟夫环
{
 pList l1;
 int i = 0;
 InitLinkList(&l1);
 for (i = 1; i <= 41; i++)
 {
  PushBack(&l1, i);
 }
 pLinkNode pos = Find(l1,41);  //查找元素,Find函数在单链表的实现中
 pos->next = l1;     //构建环
 pos=JoseCycle(&l1,3);   
 printf(" %d \n", pos->data);
 printf(" %d \n", pos->next->data);
}

wKioL1aghJCyB3L-AAAMWFm-MV8306.png