1 问题描述
选择问题(Selection Problem):有n个整数,给定一个整数k,找出n个数中第k大的数。
我们可以先从以下角度进行思考:
- n和k的大小关系是怎么样的?
- n个整数的分布情况是怎么样的?
对于第1点,如果k比n大,答案显然不存在;对于第2点,如果n个整数的分布比较特殊(例如都在0到10之间),那么可能会影响到解题策略。这些情况需要事先和面试官确认。
2 不同的解法
确认好以后就可以开始做题啦!我们假定答案总是存在的,至于n个整数的分布如何影响解题策略,下面慢慢说。
2.1 算法1
算法描述:将n个数都读进一个数组,对这个数组排序,然后返回第(n-k)个位置的数。
这种做法的开销主要来自两方面:首先是需要用一个数组写入n个数,其次是排序。前者要O(n)时间;而对于后者,可以证明:任何基于比较的排序算法的最优下界是O(nlogn)(参见:https://www.geeksforgeeks.org/lower-bound-on-comparison-based-sorting-algorithms/)。所以如果使用基于比较的排序,总的时间复杂度是O(n + nlogn) = O(nlogn)。
下面给出一个具体例子,排序算法是快速排序:
def qsort(L):
if len(L) <= 1: return L
return qsort([lt for lt in L[1:] if lt < L[0]]) + \
L[0:1] + \
qsort([ge for ge in L[1:] if ge >= L[0]])
def solve(arr, k):
return qsort(arr)[len(arr) - k]
# Test here
class TestCase:
def __init__(self, arr, k):
self.arr = arr
self.k = k
def test():
case1 = TestCase([3,1,4,1,5,9,2,6,5,3], 1)
case2 = TestCase([1,2,3,4,5,6,7,8,9,10], 1)
assert(solve(case1.arr, case1.k) == 9)
assert(solve(case2.arr, case2.k) == 10)
print("Accepted!")
if __name__=="__main__":
test()
这样,算法的平均时间复杂度和最好时间复杂度是O(nlogn),但在最坏情况下是O(n^2)。关于常见排序算法的性能,可以参见下表: