top k问题python解

题目来源:Leetcode 215 数组中第K个最大的元素

题目描述:

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2

输出: 5

1.排序方法

复杂度为O(nlogn),可以直接调用python的排序函数做(用时52ms):

class Solution:
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        nums.sort()
        return nums[-k]
        

自己手写快排(用时284ms,咋这么慢。。。):

class Solution:
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        import random
        self.quickSort(nums, 0, len(nums) - 1)
        return nums[-k]
    
    
    def partation(self, nums, l, r):
        """随机partation"""
        change_index = random.randint(l, r)
        nums[change_index], nums[r] = nums[r], nums[change_index]
        num = nums[r]
        less_index = l - 1
        more_index = r + 1
        p = l
        while p < more_index:
            if nums[p] < num:
                less_index += 1
                nums[p], nums[less_index] = nums[less_index], nums[p]
                p += 1
            elif nums[p] == num:
                p += 1
            else:
                more_index -= 1
                nums[p], nums[more_index] = nums[more_index], nums[p]
                
        equal_area = [less_index + 1, more_index - 1]
        return equal_area
    
    def quickSort(self, nums, l, r):
        if r - l < 1:
            return
        else:
            eq_area = self.partation(nums, l, r)
            self.quickSort(nums, l, eq_area[0] - 1)
            self.quickSort(nums, eq_area[1] + 1, r)

2.选择方法

定义一个存放最大值的集合set,每遍历一次数组,找到一个不在集合中的最大数,返回第k次遍历结果,这个方法超过时了。

def findKthLargest(nums, k):
    """
    :type nums: List[int]
    :type k: int
    :rtype: int
    """
    num_set = set()
    p = 0
    while p < k:
        cur_max = -float('inf')
        for i in nums:
            if i not in num_set and i > cur_max:
                cur_max = i
                count = 0
            if i == cur_max:
                count += 1
        num_set.add(cur_max)
        p += count
    return cur_max

3.堆

建立一个包括k个数小根堆,遍历数组,如果大于小根堆的堆顶,替换,重新构建小根堆。

python自带的堆实现(52ms):

def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        import heapq
        return heapq.nlargest(k, nums)[-1]

不调用库,自己实现(112ms):

主要解决两个问题:创建堆,堆顶元素改变维

def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        #先将前k个数变成小根堆
        nums = self.heapInsert(nums, k)
        for i in range(k, len(nums)):
            if nums[i] > nums[0]:
                nums = self.heapify(nums, nums[i], k)
        return nums[0]     
        

def heapInsert(self, nums, k):
        """
        将列表转变为小根堆
        """
        for index in range(1, k):
            while nums[(index - 1) // 2] > nums[index] and index > 0:
                nums[(index - 1) // 2], nums[index] = \
                nums[index], nums[(index - 1) // 2]
                index = (index - 1) // 2
        return nums

def heapify(self, nums, new_val, k):
        """
        小根堆nums的堆顶变成new_val,重新生成小根堆
        """
        head = 0
        nums[head] = new_val
        l = 1
        while l < k:
            r = l + 1
            if r >= k:
                small = l
            else:
                if nums[l] <= nums[r]:
                    small = l
                else:
                    small = r
        
            if nums[head] < nums[small]:
                break
            nums[head], nums[small] = nums[small], nums[head]
            head = small
            l = head * 2 + 1
        return nums

4.快速选择

利用快排的思想(这个用了3000多ms,不知道为啥这么慢。。)

class Solution:
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        #这里得到的是最大的k个数字,可能是不是有序的
        topk_nums = sorted(self.partation(nums, 0, len(nums) - 1, k))
        return topk_nums[-k]
    
    def partation(self, nums, left, right, k):
        """
        大->中->小
        """
        if right <= left:
            return nums[:k]
        l = left - 1
        r = right + 1
        num = nums[right]
        p = left
        while p < r:
            if nums[p] == num:
                p += 1
            elif nums[p] > num:
                l += 1
                nums[p], nums[l] = nums[l], nums[p]
                p += 1
            else:
                r -= 1
                nums[r], nums[p] = nums[p], nums[r]
        if k <= l:
            return self.partation(nums, left, l, k)
        elif k >= r:
            return self.partation(nums, r, right, k)
        else:
            return nums[:k]

5.插入排序

前k个数字变成一个有序的序列(范围:0 ~ k - 1,从大到小),遍历后面的数字,每次遍历,将其插入到前k个数字中对应的位置(60ms)

class Solution:
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        topk_nums = nums[:k]
        topk_nums.sort()
        for i in range(k, len(nums)):
            if nums[i] > topk_nums[0]:
                insert_index = self.searchInsert(topk_nums, nums[i])
                topk_nums.insert(insert_index, nums[i])
                topk_nums.pop(0)
        return topk_nums[-k]
        
    
    def searchInsert(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        if target <= nums[0]:
            return 0
        if target > nums[-1]:
            return len(nums)

        l, r = 0, len(nums) - 1
        while r >= l:
            mid = (l + r) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] > target:
                if nums[mid - 1] < target:
                    return mid
                else:
                    r = mid - 1
            else:
                if nums[mid + 1] > target:
                    return mid + 1
                else:
                    l = mid + 1

猜你喜欢

转载自blog.csdn.net/qq_38650545/article/details/83788680
今日推荐