思路一:暴力解法
先对数组排序,然后找出第k个位置
sorted(nums)[-k]
算法的时间复杂度为 O(N log N),空间复杂度为 O(1)
思路二:利用快排思想
(https://blog.csdn.net/wenqiwenqi123/article/details/81669899)
快速排序每次把一个元素交换到正确的位置,同时把左边的都放上大的,右边都放上小的。这个算法每一次选取一个枢纽元,排序之后,查看枢纽元的位置。如果它的位置大于K,就说明,要求出前面一个子序列的第K大的元素。反之,如果小于K,就说明要求出在后面一个序列的第K - 前一个序列的长度个元素。
如此,就把这个问题改变成了一个可以用快排思想解决的问题。对于快速排序,算法复杂度是O(N*logN)。而这个算法的算法复杂度是O(N)。为什么呢?
其实这个地方的算法复杂度分析很有意思。第一次交换,算法复杂度为O(N),接下来的过程和快速排序不同,快速排序是要继续处理两边的数据,再合并,合并操作的算法复杂度是O(1),于是总的算法复杂度是O(N*logN)(可以这么理解,每次交换用了N,一共logN次)。但是这里在确定枢纽元的相对位置(在K的左边或者右边)之后不用再对剩下的一半进行处理。也就是说第二次插入的算法复杂度不再是O(N)而是O(N/2),这不还是一样吗?其实不一样,因为接下来的过程是1+1/2+1/4+… < 2,换句话说就是一共是O(2N)的算法复杂度也就是O(N)的算法复杂度。
def quicksort(num ,low ,high): #快速排序
if low< high:
location = partition(num, low, high)
quicksort(num, low, location - 1)
quicksort(num, location + 1, high)
def partition(num, low, high):
pivot = num[low]
while (low < high):
while (low < high and num[high] > pivot):
high -= 1
while (low < high and num[low] < pivot):
low += 1
temp = num[low]
num[low] = num[high]
num[high] = temp
num[low] = pivot
return low
def findkth(num,low,high,k): #找到数组里第k个数
index=partition(num,low,high)
if index==k:return num[index]
if index<k:
return findkth(num,index+1,high,k)
else:
return findkth(num,low,index-1,k)
pai = [2,3,1,5,4,6]
# quicksort(pai, 0, len(pai) - 1)
print(findkth(pai,0,len(pai)-1,0))
思路三:插入排序
由于是要找 k 个最大的数,所以没有必要对所有数进行完整的排序。每次只保留 k 个当前最大的数就可以,然后每次对新来的元素跟当前 k 个树中最小的数比较,新元素大的话则插入到数组中,否则跳过。循环结束后数组中最小的数即是我们要找到第 k 大的数。 时间复杂度 (n-k)logk
def Find_Kth_max(array,k):
for i in range(1,k):
for j in range(i,0,-1):
if array[j] > array[j-1]:
array[j],array[j-1] = array[j-1],array[j]
else:
pass
for i in range(k,len(array)):
if array[i] > array[k-1]:
array[k-1] = array[i]
for j in range(k-1,0,-1):
if array[j] > array[j-1]:
array[j],array[j-1] = array[j-1],array[j]
else:
pass
return array[k-1]
print(Find_Kth_max([2,1,4,3,5,9,8,0,1,3,2,5],3))
思路四:堆排序
维护一个k大小的最小堆,对于数组中的每一个元素判断与堆顶的大小,若堆顶较大,则不管,否则,弹出堆顶,将当前值插入到堆中,继续调整最小堆。时间复杂度O(n * logk)
第一种:利用库函数
class Solution:
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
return heapq.nlargest(k, nums)[-1]
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
heap = []
for x in nums:
heapq.heappush(heap, x)
if len(heap) > k:
heapq.heappop(heap)
return heapq.heappop(heap) # [5,6] 从堆中弹出最小的元素
第二种:手动建堆
def heap_build(parent,heap):
child = 2*parent+1
while child<len(heap):
if child+1<len(heap) and heap[child+1]<heap[child]:
child = child+1
if heap[parent]<= heap[child]:
break
heap[parent],heap[child] = heap[child],heap[parent]
parent,child = child,2*child+1
return heap
def Find_heap_kth(array,k):
if k > len(array):
return None
heap = array[:k]
for i in range(k,-1,-1):
heap_build(i,heap)
for j in range(k,len(array)):
if array[j]>heap[0]:
heap[0] = array[j]
heap_build(0,heap)
return heap[0]
print(Find_heap_kth([2,1,4,3,5,9,8,0,1,3,2,5],6))