给定一个链表,确定它是否有一个循环。
跟进:你能不用额外的空间解决它吗?
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# @param head, a ListNode
# @return a boolean
def hasCycle(self, head):
if head:
walker, runner = head, head
while((walker.next != None) and (runner.next != None) and (runner.next.next != None)):
walker = walker.next
runner = runner.next.next
if walker == runner:
return True
return False
我已经看到很多人在100毫秒内运行他们的代码,但对我来说,时间加倍。那么如何通过两个指针的相同想法来改进我的代码。
更新: 共享代码我刚刚看到使用try ...在130ms内实现...除了......,非常有趣。
class Solution:
# @param head, a ListNode
# @return a boolean
def hasCycle(self, head):
try:
fast = head.next.next
slow = head.next
while fast != slow:
fast = fast.next.next
slow = slow.next
return True
except:
return False
答案 0 :(得分:1)
异常处理在python中通常非常快,如果抛出异常的条件非常罕见,处理异常比不断检查条件要快。
在你的代码中,你有几个条件来查看步行者或跑步者是否在列表的末尾(或接近结束) - “先看你跳跃”的方法。
相反,您可以抛弃所有这些检查,并且只是意识到当您尝试访问AttributeError
时某些东西是否会抛出next
,您就完成了寻找循环并且您可以退出并返回False
。您可以在更快的示例代码中看到它们实现了这一点,并且它通过常量检查优于方法。
如果您反汇编解决方案,也会发现不同之处:
>>> import dis
>>> a = Solution()
>>> dis.dis(a.hasCycle)
5 0 LOAD_FAST 1 (head)
3 POP_JUMP_IF_FALSE 114
6 6 LOAD_FAST 1 (head)
9 LOAD_FAST 1 (head)
12 ROT_TWO
13 STORE_FAST 2 (walker)
16 STORE_FAST 3 (runner)
7 19 SETUP_LOOP 92 (to 114)
>> 22 LOAD_FAST 2 (walker)
25 LOAD_ATTR 0 (next)
28 LOAD_CONST 0 (None)
31 COMPARE_OP 3 (!=)
34 POP_JUMP_IF_FALSE 110
37 LOAD_FAST 3 (runner)
40 LOAD_ATTR 0 (next)
43 LOAD_CONST 0 (None)
46 COMPARE_OP 3 (!=)
49 POP_JUMP_IF_FALSE 110
52 LOAD_FAST 3 (runner)
55 LOAD_ATTR 0 (next)
58 LOAD_ATTR 0 (next)
61 LOAD_CONST 0 (None)
64 COMPARE_OP 3 (!=)
67 POP_JUMP_IF_FALSE 110
8 70 LOAD_FAST 2 (walker)
73 LOAD_ATTR 0 (next)
76 STORE_FAST 2 (walker)
9 79 LOAD_FAST 3 (runner)
82 LOAD_ATTR 0 (next)
85 LOAD_ATTR 0 (next)
88 STORE_FAST 3 (runner)
10 91 LOAD_FAST 2 (walker)
94 LOAD_FAST 3 (runner)
97 COMPARE_OP 2 (==)
100 POP_JUMP_IF_FALSE 22
11 103 LOAD_GLOBAL 2 (True)
106 RETURN_VALUE
107 JUMP_ABSOLUTE 22
>> 110 POP_BLOCK
111 JUMP_FORWARD 0 (to 114)
12 >> 114 LOAD_GLOBAL 3 (False)
117 RETURN_VALUE
并将其与更快的试用/除外版本进行比较:
>>> b = Solution()
>>> dis.dis(b.hasCycle)
18 0 SETUP_EXCEPT 69 (to 72)
19 3 LOAD_FAST 1 (head)
6 LOAD_ATTR 0 (next)
9 LOAD_ATTR 0 (next)
12 STORE_FAST 2 (fast)
20 15 LOAD_FAST 1 (head)
18 LOAD_ATTR 0 (next)
21 STORE_FAST 3 (slow)
22 24 SETUP_LOOP 37 (to 64)
>> 27 LOAD_FAST 2 (fast)
30 LOAD_FAST 3 (slow)
33 COMPARE_OP 3 (!=)
36 POP_JUMP_IF_FALSE 63
23 39 LOAD_FAST 2 (fast)
42 LOAD_ATTR 0 (next)
45 LOAD_ATTR 0 (next)
48 STORE_FAST 2 (fast)
24 51 LOAD_FAST 3 (slow)
54 LOAD_ATTR 0 (next)
57 STORE_FAST 3 (slow)
60 JUMP_ABSOLUTE 27
>> 63 POP_BLOCK
26 >> 64 LOAD_GLOBAL 1 (True)
67 RETURN_VALUE
68 POP_BLOCK
69 JUMP_FORWARD 8 (to 80)
27 >> 72 POP_TOP
73 POP_TOP
74 POP_TOP
28 75 LOAD_GLOBAL 2 (False)
78 RETURN_VALUE
79 END_FINALLY
>> 80 LOAD_CONST 0 (None)
83 RETURN_VALUE
你可以看到后者的整体指令较少,而且分支较少(POP_JUMP_IF_FALSE)也很昂贵。