左神算法书籍《程序员代码面试指南》——2_05环形单链表的约瑟夫问题

【题目】
据说著名犹太历史学家Josephus有过以下故事:
在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,
39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,
由第1个人开始报数,报数到3的人就自杀,然后再由下一个人重新报1,报数到3的人再自杀,
这样依次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。
这就是著名的约瑟夫问题。现在请用单向环形链表描述该结构并呈现整个自杀过程。

输入:一个环形单向链表的头节点head和报数的值m。
返回:最后生存下来的节点,且这个节点自己组成环形单向链表,其他节点都删掉。
进阶:
如果链表节点数为N,想在时间复杂度为O(N)时完成原问题的要求,该怎么实现?

【题解】
整个进阶解法的过程总结为:
1.遍历链表,求链表的节点个数记为n,时间复杂度为O(N)。
2.根据n和m的值,还有上文分析的Num(i - 1)和Num(i)的关系,递归求生存节点的编号;这一步的具体过程请参看如下代码中的getLive方法,getLive方法为单决策的递归函数,且递归为N层,所以时间复杂度为O(N)。
3.最后根据生存节点的编号,遍历链表找到该节点,时间复杂度为O(N)。
4.整个过程结束,总的时间复杂度为O(N)。

 

 1 #include<iostream>
 2 
 3 using namespace std;
 4 
 5 struct Node
 6 {
 7     int val;
 8     Node* next;
 9     Node(int a = 0) :val(a), next(NULL) {}
10 };
11 
12 //第一种方法:单链表循环
13 void findTheMan(Node* head, int k)
14 {
15     Node*p  = head;
16     while (p->next)
17         p = p->next;
18     p->next = head->next;
19     p = head;
20     while(p->next!=p)
21     {
22         for (int i = 0; i < k-1; ++i)
23             p = p->next;    
24         cout << p->next->val << " ";
25         p->next = p->next->next;
26     }
27     cout << endl << "The last man: " << p->val << endl;
28 }
29 
30 //进阶解法:
31 int  getLive(int tmp, int k)
32 {
33     if (tmp == 1)
34         return 1;
35     return (getLive(tmp - 1, k) + k - 1) % tmp + 1;
36 }
37 
38 void getTheMan(Node* head, int k)
39 {
40     Node* p = head->next;
41     int tmp = 0;
42     while (p)
43     {
44         tmp++;
45         p = p->next;
46     }
47     tmp = getLive(tmp, k);
48     while (tmp--)
49         head = head->next;
50     cout << head->val << endl;
51 }
52 int main()
53 {
54     Node* head = new Node();
55     Node* p = head;
56     for (int i = 1; i < 42; ++i)
57     {
58         Node*q = new Node(i);
59         p->next = q;
60         p = q;
61     }
62     //findTheMan(head,3);
63     getTheMan(head, 3);
64 
65     return 0;
66 }

 

转载于:https://www.cnblogs.com/zzw1024/p/11227728.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值