链表问题---环形单链表的约瑟夫问题
2017-10-13 12:45
387 查看
【题目】
据说著名犹太历史学家Josephus有过如下故事:在罗马人占领乔塔帕特后,39个犹太人和Josephus及他的朋友躲进一个洞里,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第一个人开始报数,报数到3的人就自杀,再由下一个人重新报1,报数到3的人就自杀,这样依次下去,知道剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向链表描述该结构并呈现整个自杀过程。
输入:一个环形单向链表的头节点head和报数的值m
返回:最后生存下来的节点,且这个节点自己组成环形单向链表,其他节点都删掉。
进阶:如果链表节点数为N,想在时间复杂度为O(N)时完成原问题的要求,该怎么实现?
【基本思路】
普通解法。
1、在环形链表中遍历每个节点,不断转圈,不断让每个节点报数。
2、当报数为m时,就删除当前报数的节点。
3、删除节点后把剩下的节点继续连成一个环,继续转圈报数,继续删除。
4、不断的删除,直到只留下一个节点,过程结束。
普通解法删除一个节点需要遍历m次,一共要删除n-1个节点,所以总的时间复杂度为O(n*m)。
进阶解法。
普通解法之所以复杂度高,是因为我们不知道到底哪个节点会留下来。所以依靠不断的遍历删除,直到只留下一个节点。如果我们能不通过遍历,而是直接算出最后活下来的节点是哪个,就可以降低时间复杂度。
如何计算呢?首先如果环形链表的节点数为n,那么从头节点开始编号,头节点编号为1,下一个编号为2……最后一个节点编号为n。然后考虑如下:
如果只剩下一个节点,那么幸存的节点就是该节点,编号为1,即Live(1) = 1
如果剩下两个节点,幸存的节点为Live(2)
如果剩下三个节点,幸存的节点为Live(3)
如果剩下i-1个节点,幸存的节点为Live(i-1)
如果剩下i个节点,幸存的节点为Live(i)
如果剩下n个节点,幸存的节点为Live(n)
已经知道live(1) = 1,如果再确定Live(i-1)和Live(i)到底是什么关系,我们就可以通过递归过程求出Live(n)。
首先我们分析如下问题:如果一个节点数为n的链表,编号从头节点到末尾为1~n,如果删除编号为s的一个节点,那么剩下的节点的编号将会如何变化,如图所示。
设原链表的编号为y,删除一个节点后的编号为x,我们可以得到如下的公式 y = (x + s - 1) % n + 1。因此,我们可以根据Live(i-1)以及被删除的节点编号求得Live(i)的值。那么现在的问题就变成了如何求被删除节点的编号。
如图所示。对于每一个节点,如果报数值还不到m,就会一直报数下去,1~n~2n……由图我们可以得到一个报数值A与编号B的关系,即B = (A-1) % n + 1。如果报数报到m,报数的节点就是要删除的节点,那么该节点的编号根据公式可以得出 s = (m-1) % n + 1。
得到被删除节点的编号s,我们就可以得到Live(i-1)与Live(i)之间的关系:Live(i) = (Live(i-1) + s - 1) % i + 1。其中Live(i-1)可以通过向上递归得到,s = (m-1) % i + 1。两式合并后,结果为 Live(i) = (Live(i-1) + m - 1) % i + 1。
整个过程总结如下:
1、遍历链表,得到链表的节点数n,O(n)
2、根据n和m的值,以及上文推导的Live(i)与Live(i-1)的关系,递归求得幸存节点的编号。该递归是单决策递归且递归为n层,所以时间复杂度为O(n)
3、根据得到的幸存节点的编号,遍历链表找到该节点,O(n)
所以总体时间复杂度为O(n)。
【代码实现】
据说著名犹太历史学家Josephus有过如下故事:在罗马人占领乔塔帕特后,39个犹太人和Josephus及他的朋友躲进一个洞里,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第一个人开始报数,报数到3的人就自杀,再由下一个人重新报1,报数到3的人就自杀,这样依次下去,知道剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向链表描述该结构并呈现整个自杀过程。
输入:一个环形单向链表的头节点head和报数的值m
返回:最后生存下来的节点,且这个节点自己组成环形单向链表,其他节点都删掉。
进阶:如果链表节点数为N,想在时间复杂度为O(N)时完成原问题的要求,该怎么实现?
【基本思路】
普通解法。
1、在环形链表中遍历每个节点,不断转圈,不断让每个节点报数。
2、当报数为m时,就删除当前报数的节点。
3、删除节点后把剩下的节点继续连成一个环,继续转圈报数,继续删除。
4、不断的删除,直到只留下一个节点,过程结束。
普通解法删除一个节点需要遍历m次,一共要删除n-1个节点,所以总的时间复杂度为O(n*m)。
进阶解法。
普通解法之所以复杂度高,是因为我们不知道到底哪个节点会留下来。所以依靠不断的遍历删除,直到只留下一个节点。如果我们能不通过遍历,而是直接算出最后活下来的节点是哪个,就可以降低时间复杂度。
如何计算呢?首先如果环形链表的节点数为n,那么从头节点开始编号,头节点编号为1,下一个编号为2……最后一个节点编号为n。然后考虑如下:
如果只剩下一个节点,那么幸存的节点就是该节点,编号为1,即Live(1) = 1
如果剩下两个节点,幸存的节点为Live(2)
如果剩下三个节点,幸存的节点为Live(3)
如果剩下i-1个节点,幸存的节点为Live(i-1)
如果剩下i个节点,幸存的节点为Live(i)
如果剩下n个节点,幸存的节点为Live(n)
已经知道live(1) = 1,如果再确定Live(i-1)和Live(i)到底是什么关系,我们就可以通过递归过程求出Live(n)。
首先我们分析如下问题:如果一个节点数为n的链表,编号从头节点到末尾为1~n,如果删除编号为s的一个节点,那么剩下的节点的编号将会如何变化,如图所示。
设原链表的编号为y,删除一个节点后的编号为x,我们可以得到如下的公式 y = (x + s - 1) % n + 1。因此,我们可以根据Live(i-1)以及被删除的节点编号求得Live(i)的值。那么现在的问题就变成了如何求被删除节点的编号。
如图所示。对于每一个节点,如果报数值还不到m,就会一直报数下去,1~n~2n……由图我们可以得到一个报数值A与编号B的关系,即B = (A-1) % n + 1。如果报数报到m,报数的节点就是要删除的节点,那么该节点的编号根据公式可以得出 s = (m-1) % n + 1。
得到被删除节点的编号s,我们就可以得到Live(i-1)与Live(i)之间的关系:Live(i) = (Live(i-1) + s - 1) % i + 1。其中Live(i-1)可以通过向上递归得到,s = (m-1) % i + 1。两式合并后,结果为 Live(i) = (Live(i-1) + m - 1) % i + 1。
整个过程总结如下:
1、遍历链表,得到链表的节点数n,O(n)
2、根据n和m的值,以及上文推导的Live(i)与Live(i-1)的关系,递归求得幸存节点的编号。该递归是单决策递归且递归为n层,所以时间复杂度为O(n)
3、根据得到的幸存节点的编号,遍历链表找到该节点,O(n)
所以总体时间复杂度为O(n)。
【代码实现】
#python3.5 def josephusKill1(head, m): if head == None or head.next == None or m < 1: return head pre = head while pre.next != head: pre = pre.next count = 1 while head != pre: if count != m: head = head.next pre = pre.next count += 1 else: pre.next = head.next head = pre.next count = 1 return head def josephusKill2(head, m): def getLive(n, m): if n == 1: return 1 return (getLive(n-1, m) + m - 1) % n + 1 if head == None or head.next == None or m < 1: return head n = 1 cur = head while cur.next != head: n += 1 cur = cur.next n = getLive(n, m) while n-1 != 0: n -= 1 head = head.next head.next = head return head
相关文章推荐
- 环形单链表的约瑟夫问题
- 环形链表解决约瑟夫游戏问题
- php实现单,双向链表,环形链表解决约瑟夫问题
- 构造数组MaxTree、环形单链表的约瑟夫问题等总结
- Java环形链表--约瑟夫问题
- 使用环形链表解决约瑟夫(丢手帕)问题
- 链表之单链表约瑟夫问题(一)
- 环形单链表的约瑟夫问题
- 数据结构 环形单链表的约瑟夫问题
- 环形链表+约瑟夫问题实现
- 环形单链表的约瑟夫问题
- 环形单链表的约瑟夫问题
- php使用环形链表解决约瑟夫问题完整示例
- 环形链表实现约瑟夫问题 java语言
- python环形单链表的约瑟夫问题详解
- 链表之单链表约瑟夫问题(二)
- 环形单链表的约瑟夫问题 Python版
- 17.环形链表,以及用环形链表解决约瑟夫问题
- 判断单链表是否存在环,判断两个链表是否相交问题详解(转)
- 【单链表面试题】-----链表带环问题