python 为什么这段代码花费的时间比预期的要长?[关闭]

1yjd4xko  于 2023-04-28  发布在  Python
关注(0)|答案(3)|浏览(181)

已关闭,此问题需要更focused。目前不接受答复。
**想改善这个问题吗?**更新问题,使其仅通过editing this post关注一个问题。

10小时前关闭。
Improve this question

**问题:**给定一个未排序元素的列表,我们必须找到最长连续元素序列的长度。

时间复杂度:O(N)

用于Ex:[4,7,1,100,28,2,3]
**输出:**4,因为最长的连续元素序列是上述列表中的[1,2,3,4]

View Problem Statement
我用 Python 写了代码。我使用了set和**map(defaultdict)**来解决这个问题。

from collections import defaultdict as maps
class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        nums = set(nums); # to remove duplicates
        n = len(nums);
        if n == 0: return 0;
        if n == 1: return 1;
        present = maps(int);
        # Inserting values into map (defaultdict) as key
        for i in nums:
            present[i] = 1; # to check for presence of element
        i = 0; curr = 0; cnt, maxx = 0, 0; visset = {i for i in nums}
        while n > 0:
              #I had to use this while loop since the for-loop below will get interrupted if we make any changes to the iterating set.
            
            cnt = 1;
            for ind in visset:
                curr = ind; n -= 1; 
                visset.remove(ind); break; #remove is O(1) complexity for set
            # values less than curr::
            curr, temp = curr, curr;
            while n > 0:
                if present[curr - 1]:
                    curr -= 1; n -= 1; 
                    visset.remove(curr); cnt += 1; #remove is O(1) complexity for set
                else:
                    break;
            # values greater than curr::
            while n > 0:
                if present[temp + 1]:
                    temp += 1; n -= 1; 
                    visset.remove(temp); cnt += 1; #remove is O(1) complexity for set
                else:
                    break;
            maxx = max(cnt, maxx);
        maxx = max(cnt, maxx);
        return maxx

for more reference and runtime
有人能解释一下为什么这段代码的运行时间比预期的要高吗?

**UPDATE:**这个程序的运行时间大约是7000ms,如果我用字典替换set,并且在set的情况下使用del dictt[i]而不是remove(),那么运行时间将下降到2000ms

ia2d9nvy

ia2d9nvy1#

它在像list(range(0, 200000, 2))这样的输入上很慢。这样做100000次:

for ind in visset:
    curr = ind; n -= 1; 
    visset.remove(ind); break; #remove is O(1) complexity for set

不是删除速度慢,而是for循环迭代找到ind,正如这里所解释的。总共需要2次时间。
使用ind = visset.pop()代替,它针对重复删除任意元素进行了优化。或者您可以使用collections.OrderedDict而不是您的设备。由于它的额外的链表,它的快速为您的风格找到一个和删除它。
这里有一个替代方法,你也可以考虑。
基于我们可以测试前一个数字和下一个数字是否在列表中的事实,我们可以构建并跟踪项目的运行。

def longest_consecutive(numbers):
    numbers = set(numbers)
    best_length = 0
    best_start = -1

    for number in numbers:
        ## ----------------
        ## this run is already/will be accounted for by some real 'start'
        ## ----------------
        if number - 1 in numbers:
            continue
        ## ----------------

        ## ----------------
        ## find consecutive numbers
        ## ----------------
        i = number
        while i in numbers:
            i += 1
        ## ----------------

        ## ----------------
        ## record the streak if better than what we have seen
        ## ----------------
        if i - number > best_length:
            best_length = i - number
            best_start = number
        ## ----------------

    return list(range(best_start, best_start + best_length))

基于timeit,这将在大约1/4秒内完成一百万个随机整数的列表。

hfyxw5xn

hfyxw5xn2#

问题可能源于for ind in visset。我以前也是这样做的,但我从来没有想过要这样做。在从集合中删除元素之后,迭代集合似乎会涉及一些开销。

from time import time

s = set(range(1_000_000))
x = set(range(1_000_000))
start = time()
for i in range(100_000):
    x.remove(i)
    for i in s: break
print(time()-start)  # 0.017419099807739258

start = time()
for i in range(100_000):
    s.remove(i)
    for i in s: break
print(time()-start)  # 2.6430912017822266

也许不删除项的解决方案会运行得更快:

def longestConsec(nums):
    forward  = dict(zip(nums,nums))
    backward = dict(zip(nums,nums))
    for n in nums:
        f = forward.get(n+1,n)
        b = backward.get(n-1,n)
        forward[b]  = f
        backward[f] = b            
    return max(f-b+1 for f,b in backward.items())
wz3gfoph

wz3gfoph3#

你的在leetcode上没有修改就通过了,但它的速度较慢。下面是对相同代码的简化,它的性能略高于平均水平:

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        present = set(nums)
        curr = 0
        cnt, maxx = 0, 0
        while present:
            cnt = 1
            start = curr = present.pop()
            while start - 1 in present:
                start -= 1
                cnt += 1
                present.remove(start)
            while curr + 1 in present:
                curr += 1
                cnt += 1
                present.remove(curr)
            maxx = max(cnt, maxx)
        return maxx

您确实有很多不必要的数据结构,并且在值甚至没有使用的地方复制Map。我把它们都换成了一套。

相关问题