八叶一刀·无仞剑

万物流转,无中生有,有归于无

导航

第K小的数

Posted on 2021-01-09 22:21  闪之剑圣  阅读(413)  评论(0编辑  收藏  举报

给出一个数组,从中找到第K小的数,这可以算是一个经典的算法题了。今天在这里给出几种解法:
1.最简单的方法,就是对数组进行排序,然后选取其中前K个数即可。排序算法一般我们可以用快速排序,这样的话时间复杂度就是O(NlgN)。但是以上这种方法还是显得有些慢,那有没有比它更快的方法呢?
2.参考快速排序,快排在递归的过程中,需要不断将数组分为两部分,且一部分总是小于另外一部分,我们可以用这种方法对数组进行递归,直到分出前K个元素作为几个集合为止。代码如下:

def partition(numbers, begin, end):
    assert begin <= end
    if begin == end:
        return begin
    mid = numbers[begin]
    index = begin + 1
    for i in xrange(begin+1, end+1):
        if numbers[i] > mid:
            continue
        else:
            tmp = numbers[i]
            numbers[i] = numbers[index]
            numbers[index] = tmp
            index += 1
    tmp = numbers[index-1]
    numbers[index-1] = mid
    numbers[begin] = tmp
    return index-1
    
def getLeastKnumber(numbers, k):
    l = len(numbers)
    begin = 0
    end = l-1
    index = partition(numbers, begin, end)
    while index != k-1:
        if index < k-1:
            begin = index + 1
        else:
            end = index - 1
        index = partition(numbers, begin, end)
    return numbers[index]

该方法的时间复杂度为O(N)。
3.以上的两个方法都需要对所有数据遍历多次,如果数据量太大,都没办法全部装进内存了,此时也就没法对数组排序,那么此时该怎么做呢?
我们可以先拿出前K个元素建一个最大堆,堆顶元素是其中最大的数。然后遍历数组其他元素,与堆顶的元素进行比较。
有这么几种情况:
遍历的数>=堆顶元素,那么可以跳过,因为我们要寻找第K小的数,遍历的数比K个数中最大那个都要大,那它肯定不是第K小的数(有K个数都要比它小呢)。
遍历的数<堆顶元素,那么这个数和堆顶元素互换,并重新维护堆。
一直到遍历结束,取出堆顶的数就是第K小的数了。
该算法的时间复杂度为O(NlgK)
这个逻辑比较简单,就不写代码了。