简体   繁体   English

程序超出时间限制 - 找到第 K 个最小元素

[英]Time limit excedded for program - find Kth smallest element

I am looking at GeeksForGeeks problem Kth smallest element :我正在查看 GeeksForGeeks 问题第 K 个最小元素

Given an array arr[] and an integer K where K is smaller than size of array, the task is to find the Kth smallest element in the given array.给定一个数组arr[]和一个整数K ,其中 K 小于数组的大小,任务是找到给定数组中第 K 个最小的元素。 It is given that all array elements are distinct.假设所有数组元素都是不同的。

Expected Time Complexity: O(n)预期时间复杂度: O(n)
Expected Auxiliary Space: O(log(n))预期辅助空间: O(log(n))
Constraints:约束:
1 <= N <= 105 1 <= N <= 105
1 <= arr[i] <= 105 1 <= arr[i] <= 105
1 <= K <= N 1 <= K <= N

My Code:我的代码:

class Solution:
    def kthSmallest(self,arr, l, r, k):
        '''
        arr : given array
        l : starting index of the array i.e 0
        r : ending index of the array i.e size-1
        k : find kth smallest element and return using this function
        '''
        arr2=arr[:k]
        arr2.insert(0,None)
        for i in range(k//2,0,-1):
            arr2=self.heapify(arr2,i,k-1)
            
        for i in arr[k:]:
            if i <arr2[1]:
                arr2[1]=i
                arr2=self.heapify(arr2,1,k-1)
        return arr2[1]


    def heapify(self,arr, i, r):
        if 2 * i <= r + 1 and arr[2 * i] > arr[i]:
            arr[2 * i], arr[i] = arr[i], arr[i * 2]
            arr = self.heapify(arr, 2 * i, r)
        if 2 * i + 1 <= r + 1 and arr[2 * i + 1] > arr[i]:
            arr[2 * i + 1], arr[i] = arr[i], arr[i * 2 + 1]
            arr = self.heapify(arr, 2 * i + 1, r)
        return arr

I made a sub array of first K elements in the array, and max heapified it.我在数组中创建了一个包含前 K 个元素的子数组,并对其进行了最大处理。 Then for the rest of the elements in the array, if the element is smaller than the first element of the heap, I replaced the top element and then max heapified the top element.然后对于数组中的其余元素,如果该元素小于堆的第一个元素,我替换了顶部元素,然后将顶部元素最大化。 I am getting time limit exceeded error.我收到超出时间限制的错误。 Any idea?任何想法?

The problem is that your heapify function is not efficient.问题是您的heapify函数效率不高。 In the worst case it makes two recursive calls at the same recursion depth.在最坏的情况下,它会以相同的递归深度进行两次递归调用。 This may even happen at several recursion depths, so that the number of times heapify is called recursively could become quite large.这甚至可能发生在几个递归深度,因此递归调用heapify的次数可能会变得非常大。 The goal is to have this only call heapify once (at the most) per recursion level.目标是让每个递归级别只调用一次heapify (最多)。

It should first find the greatest child, and only then determine whether heapify should be called again, and make that single call if needed.它应该首先找到最大的孩子,然后才确定是否应该再次调用heapify ,并在需要时进行单次调用。

Some other remarks:其他一些评论:

  • Instead of making heapify recursive, use an iterative solution.与其使heapify递归,不如使用迭代解决方案。 This will also save some execution time.这也将节省一些执行时间。

  • It is strange to pass k-1 to heapify as last argument, when the last element sits at index k , and so you get the weird comparison <= r + 1 in that function.当最后一个元素位于索引k处时,将k-1作为最后一个参数传递给heapify很奇怪,因此您在该函数中得到了奇怪的比较<= r + 1 It is more intuitive to pass k as argument, and work with <= r inside the function.k作为参数传递更直观,并在函数内部使用<= r

  • As arr is mutated by heapify it is not needed to return it.由于arrheapify突变,因此不需要返回它。 This is just overhead that is useless.这只是无用的开销。

  • 2 * i is calculated several times. 2 * i被计算了几次。 It is better to calcuate this only once.最好只计算一次。

  • arr[k:] makes a copy of that part of the list. arr[k:]复制列表的该部分。 This is not really needed.这并不是真正需要的。 You could just iterate over the range and take the corresponding value from the array in the loop.您可以迭代范围并从循环中的数组中获取相应的值。

  • It is not clear why the main function needs to get l and r as arguments, since in comments it is explained that l will be 0 and r the index of the last element.不清楚为什么 main 函数需要获取lr作为参数,因为在注释中解释了l将是 0 并且r是最后一个元素的索引。 But in my opinion, since you get them, you should use them.但在我看来,既然你得到了它们,你就应该使用它们。 So you should not assume l is 0,... etc.所以你不应该假设l是 0,... 等等。

  • I would use a more descriptive name for arr2 .我会为arr2使用更具描述性的名称。 Why not name it heap ?为什么不将其命名为heap

Here is the improvement of your code:这是您的代码的改进:

class Solution:
    def kthSmallest(self,arr, l, r, k):
        '''
        arr : given array
        l : starting index of the array i.e 0
        r : ending index of the array i.e size-1
        k : find kth smallest element and return using this function
        '''
        heap = [arr[i] for i in range(l, r + 1)]
        heap.insert(0, None)
        for i in range(k//2, 0, -1):
            self.heapify(heap, i, k)
        for i in range(l + k, r + 1):
            val = arr[i]
            if val < heap[1]:
                heap[1] = val
                self.heapify(heap, 1, k)
        return heap[1]

    def heapify(self, arr, i, r):
        child = 2 * i
        while child <= r:
            if child + 1 <= r and arr[child + 1] > arr[child]:
                child += 1
            if arr[child] <= arr[i]:
                break
            arr[child], arr[i] = arr[i], arr[child]
            i = child
            child = 2 * i

Finally, there is a heapq module you can use, which simplifies your code:最后,您可以使用一个heapq模块,它可以简化您的代码:

from heapq import heapify, heapreplace

class Solution:
    def kthSmallest(self, arr, l, r, k):
        '''
        arr : given array
        l : starting index of the array i.e 0
        r : ending index of the array i.e size-1
        k : find kth smallest element and return using this function
        '''
        heap = [-arr[i] for i in range(l, l + k)]
        heapify(heap)
            
        for i in range(l + k, r + 1):
            val = -arr[i]
            if val > heap[0]:
                heapreplace(heap, val)
        return -heap[0]

The unary minus that occurs here and there is to make the native minheap work as a maxheap.在这里和那里发生的一元减号是为了使本机 minheap 作为 maxheap 工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM