是什么让我的python代码如此慢(链接列表循环)?

时间:2015-05-30 13:16:18

标签: python data-structures

  

给定一个链表,确定它是否有一个循环。

     

跟进:你能不用额外的空间解决它吗?

# 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

1 个答案:

答案 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)也很昂贵。