题解
代码
- TopK快排
class Solution:
def getLeastNumbers(self, nums, k: int):
def random_quick(l, r, k):
random_idx = random.randint(l, r)
nums[r], nums[random_idx] = nums[random_idx], nums[r]
pivot = nums[r] # 固定中枢值pivot为右边界值
i = l - 1 # i从左边界开始
for j in range(l, r):
# if nums[j] > pivot: TopK使用
if nums[j] <= pivot: # 若当前值小于pivot,则与i的元素交换并放在pivot左侧,MinK使用
i += 1
nums[i], nums[j] = nums[j], nums[i]
nums[i + 1], nums[r] = nums[r], nums[i + 1] # 交换中枢值pivot到正确位置
pos = i + 1 # 本次排好的中枢值元素下标
val = pos - l + 1 # 计算当前值是第多少小的数,val表示第pos - l + 1小的数
if k < val: random_quick(l, pos - 1, k) # 第k小的数在pivot左侧
elif k > val: random_quick(pos + 1, r, k - val) # 第k小的数在pivot右侧
########## MinK
if k == 0: return []
random_quick(0, len(nums)-1, k)
return nums[:k] # MinK,但返回是无序的!
########## TopK
nums = [-n for n in nums]
if k == 0: return []
random_quick(0, len(nums)-1, k)
nums = [-n for n in nums]
return nums[:k] # TopK,但返回是无序的!
- TopK堆排序
# topK堆排序
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
if k == 0: return []
hp = [-x for x in arr[:k]]
heapq.heapify(hp)
for i in range(k, len(arr)):
if -hp[0] > arr[i]:
heapq.heappop(hp)
heapq.heappush(hp, -arr[i])
return [-x for x in hp]
# topK堆排序
class Solution:
def getLeastNumbers(self, nums: List[int], k: int) -> List[int]:
def buildHeap(heap):
for i in range(len(heap) // 2, -1, -1):
heapify(heap, i)
def heapify(arr, i):
length = len(arr)
left, right, largest = 2 * i + 1, 2 * i + 2, i
if left < length and arr[left] < arr[largest]:
largest = left
if right < length and arr[right] < arr[largest]:
largest = right
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, largest)
if k == 0: return []
######################################################
# TopK:小小小顶堆
heap = nums[:k]
buildHeap(heap) # 构建小顶堆
for i in range(k, len(nums)):
# 若当前元素 > 堆顶元素,则调整堆
if nums[i] > heap[0]:
heap[0] = nums[i]
heapify(heap, 0) # 这里只需调整堆顶
return heap # TopK,但返回是无序的!
######################################################
# MinK:大大大顶堆
heap = [-num for num in nums[:k]]
buildHeap(heap) # 构建大顶堆
for i in range(k, len(nums)):
# 若当前元素取反 > 堆顶元素,则调整堆
if -nums[i] > heap[0]:
heap[0] = -nums[i] # 取反入堆!
heapify(heap, 0) # 这里只需调整堆顶
return [-num for num in heap] # MinK,但返回是无序的!
分隔线
- Python排序算法
class Solution:
### 1109 插入排序(超时)
# 从第二个元素开始和前面的元素进行比较(逆序比较),如果前面的元素比当前元素大,则将前面元素后移一位;当前元素继续往前比较,直到找到比它小或等于它的元素并插入在其后面
# 然后选择第三个元素,重复上述操作,进行插入,依次选择到最后一个元素,插入后即完成所有排序
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
def insertionSort(arr):
for i in range(1, len(arr)):
curr = arr[i]
pre_idx = i - 1
while pre_idx >= 0 and arr[pre_idx] >= curr: # 若前一个元素存在且前一个元素大于当前元素
arr[pre_idx + 1] = arr[pre_idx] # 前一个元素后移一位
pre_idx -= 1 # 继续向前查找
arr[pre_idx + 1] = curr # 直到找到不大于当前元素的元素,插入其后
return arr
return insertionSort(arr)[: k]
### 1109 选择排序(超时)
# 设第一个元素为比较元素,依次和后面的元素比较,比较完所有元素找到最小的元素,将它和第一个元素互换,重复上述操作
# 找出第二小的元素和第二个位置的元素互换,以此类推找出剩余最小元素将它换到前面,即完成排序
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
def selectionSort(arr):
for i in range(len(arr) - 1): # 从第一个元素到倒数第二个元素
min_idx = i # 每轮假设第一个元素为最小元素
for j in range(i, len(arr)): # 遍历i之后的所有元素,若存在比当前最小元素更小的元素,则更新
if arr[min_idx] > arr[j]:
min_idx = j # 更新最小元素下标
arr[min_idx], arr[i] = arr[i], arr[min_idx] # 每轮结束后,交换当前元素i和找到的最小元素
return arr
return selectionSort(arr)[: k]
### 1109 冒泡排序(超时)
# 从第一个和第二个开始比较,如果第一个比第二个大,则交换位置,然后比较第二个和第三个,逐渐往后,经过第一轮后最大的元素已经排在最后
# 重复上述操作的话第二大的则会排在倒数第二的位置;重复上述操作n-1次即可完成排序,因为最后一次只有一个元素所以不需要比较
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
def bubbleSort(arr):
for i in range(len(arr) - 1): # 从第一个元素到倒数第二个元素
for j in range(len(arr) - 1 - i): # 从第一个元素到未排序的n-1-i个元素,最后i个元素已排序完成
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j] # 相邻两个元素依次顺序对比,若大小顺序相反则交换
return arr
return bubbleSort(arr)[: k]
### 1109 快速排序
# 从原数组中随机选择一个值作为中枢值,数组中剩下的每一个元素与中枢值进行比较
# 小于中枢值的元素存放到一个子数组,大于中枢值的元素存放到另一个子数组,对子数组递归进行上述操作,直至子数组长度为0或1时返回子数组,开始返回递归
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
def quickSort(arr):
# 若子数组只含有0个或1个元素,则默认为有序数组,直接返回
if len(arr) < 2:
return arr
## 以下3中初始化中枢值的方法,选择一种执行即可
pivot_idx = 0 # 1.初始化中枢值为数组arr的第一个元素 (928 ms,24.1 MB)
pivot_idx = -1 # 2.初始化中枢值为数组arr的最后一个元素(248 ms,15 MB)
pivot_idx = len(arr) // 2 # 3.初始化中枢值为数组arr的中间元素 (232 ms,14.8 MB)
pivot = arr[pivot_idx]
arr.pop(pivot_idx) # 去掉中枢元素
lt = [i for i in arr if i <= pivot] # 小于pivot的子数组lt
gt = [i for i in arr if i > pivot] # 大于pivot的子数组gt
return quickSort(lt) + [pivot] + quickSort(gt) # 返回值:较小值子数组 + 中枢值 + 较大值子数组
return quickSort(arr)[: k]
### 1110 归并排序(460 ms,14.8 MB)
# 使用二分法将原数组不断两两划分,直至达到原子数组,然后返回递归;在返回过程中对子数组进行排序,再两两合并,直至合并到原数组长度(分而治之)
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
def mergeSort(arr):
# 达到原子数组,直接返回
if len(arr) < 2:
return arr
# 确定中间元素下标,二分法
mid = len(arr) // 2
# 根据中间元素将原数组划分为左右两个子数组,两个子数组分别进行归并排序
l_arr, r_arr = mergeSort(arr[:mid]), mergeSort(arr[mid:])
# 新建一个辅助数组
sorted_arr = []
# 当左右子数组中均不为空时,进行循环
while len(l_arr) > 0 and len(r_arr) > 0:
# 依次顺序比较左右子数组中元素大小,将小的一个存入辅助数组中
if l_arr[0] > r_arr[0]:
sorted_arr.append(r_arr.pop(0))
else:
sorted_arr.append(l_arr.pop(0))
# 追加可能剩余的元素
sorted_arr += l_arr
sorted_arr += r_arr
return sorted_arr
return mergeSort(arr)[: k]
### 1110 堆排序(356 ms,14.4 MB)
# 堆分为最大堆和最小堆,是完全二叉树;堆排序就是把堆顶的最大数取出,将剩余的堆继续调整为最大堆
# 剩余部分调整为最大堆后,再次将堆顶的最大数取出,再将剩余部分调整为最大堆,这个过程持续到剩余数只有一个时结束
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
def heapify(arr, heap_size, parent_idx):
parent_val = arr[parent_idx] # 暂存父节点元素,用于和下面的节点进行对比
child_idx = parent_idx * 2 + 1 # 计算(左)子节点下标
# 当子节点未越界时,进行循环
while child_idx < heap_size:
# 若此父节点存在右子节点,且右子节点大于左子节点,则更新子节点下标为右子节点
if child_idx + 1 < heap_size and arr[child_idx] < arr[child_idx + 1]:
child_idx += 1
# 若父节点大于或等于最大子节点,则可以停止比较,退出循环
if parent_val >= arr[child_idx]:
break
# 若父节点小于最大子节点,则替换父节点和子节点的元素和下标
arr[parent_idx] = arr[child_idx]
parent_idx = child_idx
child_idx = parent_idx * 2 + 1 # 继续循环比较下面的节点
# 若不存在大于父节点的子节点,则父节点不动;否则将原始的父节点元素下沉到叶子结点
arr[parent_idx] = parent_val
def heapSort(arr):
# 第一次对原数组构建最大堆时,因为知道所有的非叶节点,因此从最后一个非叶结点(下标为len(arr)//2 - 1)开始向上遍历全部非叶结点,无需从根节点遍历
for parent_idx in range(len(arr)//2 - 1, -1, -1):
heapify(arr, len(arr), parent_idx) # parent_idx表示非叶节点
# 第二次开始则从根节点构建最大堆(无法直接知道所有非叶节点),且每次减少一个元素
for heap_size in range(len(arr) - 1, 0, -1):
arr[0], arr[heap_size] = arr[heap_size], arr[0] # 交换最大根和最后一个元素
heapify(arr, heap_size, 0) # heap_size表示当前待排序堆的数组长度,0表示根节点
return arr
return heapSort(arr)[: k]