题意:给定一个无序的数组,寻找第K大的元素。
输入:
nums = [3,2,1,5,6,4]
k=2
pivot 的选择很重要,如果对于一个已排序的数组,我们每次都选择最大/最小的值为 pivot,那么时间复杂度为 O(N^2) 。每次通过 random 选择 pivot 可以尽量避免最坏情况发生。
快速选择算法的平均时间复杂度是 O(N),但最坏情况下的时间复杂度是 O(N^2) ,因为我们已经随机选择 pivot,所以能够最大程度上的减少最坏情况发生
def findKthLargest(self, nums, k):
def quickSelect(nums, lo, hi, k): # 从小到大排序
pivot = random.randint(lo, hi) # 0-len(nums)-1中随机取一个下标,避免最坏的情况
nums[hi], nums[pivot] = nums[pivot], nums[hi] # 最右边存这个支点
i = j = lo
while j < hi:
if nums[j] <= nums[hi]: # 找到一个比支点还小的数
nums[i], nums[j] = nums[j], nums[i] # i, j相互交换
i += 1
j += 1 # 每一次j都要前进一步
nums[i], nums[j] = nums[j], nums[i] # 把pivot放到中间来
if hi > k + i - 1: # Topk在右边
return quickSelect(nums, i + 1, hi, k)
elif hi < k + i -1: # Topk在左边
return quickSelect(nums, lo, i - 1, k - (hi - i + 1))
else:
return nums[i]
return quickSelect(nums, 0, len(nums)-1, k)
给定一个二维数组,数组中每一行代表一个点,求距离原点(0,0)最近的K个点
还是按照以上的快速选择排序算法,稍微做一点点修改:
def kClosest(self, points, K):
# 从小到大,找的是从右边数的第k大的元素
def quickSort(points, lo, hi, k):
pivot = random.randint(lo,hi) #在闭区间里面选择
points[hi], points[pivot] = points[pivot], points[hi]
i = j = lo
while j < hi: # 从左到右开始遍历
if dis(points[j]) > dis(points[hi]): # 修改1:从大往小排序
points[i], points[j] = points[j], points[i] # 交换点i,j,把小数移到左边去
i += 1
j += 1
points[i], points[j] = points[j], points[i] # 把pivot放在中间来
# 以下是根据pivot的位置来进行划分
if hi > k + i -1: # i太小了,Topk在右边
return quickSort(points, i+1, hi, k)
elif hi < k + i - 1: # Topk在左边
return quickSort(points, lo, i-1, k-(hi-i+1))
else:
return points[i:][:] # 修改2:返回最后的k个最近的点
def dis(dot): # 求得距离
return dot[0]**2 + dot[1]**2
return quickSort(points, 0, len(points)-1, K) # 要的是从左往右的点
链接:最小的k个数
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
这道题和上一道题"距离原点最近的k个节点"非常类似,因此也可以使用快速选择算法来做,并且时间复杂度很低,O(N)来做了
import random
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
if k <=0 or k > len(tinput) or not tinput: return []
def quickSelect(nums, lo, hi, k): # 从小到大排序
pivot = random.randint(lo, hi) # 0-len(nums)-1中随机取一个下标,避免最坏的情况
nums[hi], nums[pivot] = nums[pivot], nums[hi] # 最右边存这个支点
i = j = lo
while j < hi:
if nums[j] > nums[hi]: # 找到一个比支点还大的数
nums[i], nums[j] = nums[j], nums[i] # i, j相互交换,把大数放后面
i += 1
j += 1 # 每一次j都要前进一步
nums[i], nums[j] = nums[j], nums[i] # 把pivot放到中间来
if hi > k + i - 1: # Topk在右边
return quickSelect(nums, i + 1, hi, k)
elif hi < k + i -1: # Topk在左边
return quickSelect(nums, lo, i - 1, k - (hi - i + 1))
else:
return nums[i:]
res = quickSelect(tinput, 0, len(tinput)-1, k)
res.sort() # 可能退化为O(N^2)啊!
return res
通过以上快速选择算法选择的top N个元素后,原来的数组的顺序已经打乱了,使用heapq模块取出top N个元素后,不会修改原来的数组的顺序!
>>> import heapq
>>> nums=[1,8,2,23,7,-4,18,23,42,37,2]
>>> print(heapq.nlargest(3,nums)) # 从大到小排序
[42, 37, 23]
>>> print(heapq.nsmallest(3,nums)) #从小到大排序
[-4, 1, 2]