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

匿名 (未验证) 提交于 2019-12-02 23:49:02

【题目】
据说著名犹太历史学家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 }

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!