当前位置:   article > 正文

使用快速排序、堆和桶解决「TopK问题」(Python)_topk 桶排序

topk 桶排序

TopK问题即求解第K个最大or最小元素的问题,一般使用快排来实现。

1 快速排序

先总结快速排序的python模板。首先使用partition函数完成对数组从pivot的划分工作。

partition函数模板:

  1. # 将数组分为小于基准值、基准值、大于基准值三个部分
  2. def partition(nums, left, right):
  3. pivot = nums[left] # 初始化一个基准值
  4. i, j = left, right
  5. while(i < j):
  6. while(i < j and nums[j] >= pivot): j -= 1 # 从后往前查找,直到找到一个比pivot小的数
  7. nums[i] = nums[j] # 把这个数放左边来
  8. while(i < j and nums[i] <= pivot): i += 1 # 从前往后查找,直到找到一个比pivot大的数
  9. nums[j] = nums[i] # 把这个数放右边来
  10. nums[i] = pivot
  11. return i # 返回基准值最终位置
'
运行

然后使用partition递归完成快排

快排模板:

  1. def quicksort(nums, left, right):
  2. if left < right:
  3. index = partition(nums, left, right) # 一个一个固定pivot的位置,直到每个数字位置都固定好
  4. quicksort(nums, left, index - 1)
  5. quicksort(nums, index + 1, right)
'
运行

而topk问题则就是快排并不需要全部完成,只完成到第k个位置的数被固定的时候就可以停止了。

top-k切分:

将快速排序改成快速选择,也就是找到应该固定在这个数组排好序之后的第k个位置上的那个数。这个数左边是比它更小的数,右边是比它大的数。找到该位置后停止迭代,完成一次划分。

  1. def topk_split(nums, k, left, right):
  2. if left < right:
  3. index = partition(nums, left, right)
  4. if index == k: return
  5. elif index < k: topk_split(nums, k, index+1, right)
  6. else: topk_split(nums, k, left, index - 1)
'
运行

因此,可以根据topk_split解决一系列top-k问题:

获取前k小的数:

  1. def topk_small(nums, k, left, right):
  2. topk_split(nums, k, 0, len(nums) - 1)
  3. return nums[:k]
'
运行

获取前k大的数:

  1. def topk_big(nums, k, left, right):
  2. topk_split(nums, len(nums) - k, 0, len(nums) - 1) # 从len(nums)-k处划分好后右边部分就是前k大的数
  3. return nums[len(nums) - k:]
'
运行

只排序前k个小的数:

  1. def topk_sort_left(nums, k, left, right):
  2. topk_split(nums, k, 0, len(nums) - 1) # 从k划分,取前k个数快排
  3. topk = nums[:k]
  4. quicksort(topk, 0, k - 1)
  5. return topk + nums[k:]
'
运行

只排序后k个大的数:

  1. def topk_sort_right(nums, k, left, right):
  2. topk_split(nums, k, 0, len(nums) - 1)
  3. topk = nums[k:]
  4. quicksort(topk, k, len(nums) - 1)
  5. return nums[:k] + topk
'
运行

(注意python列表切片包前不包后,[start: stop: step]中,包括start但不包括stop)

2 堆

用于求解top-k问题。例如,求第k个最大元素,则建立小顶堆,不断往小顶堆中push新元素,当堆中元素数量>k时,pop堆顶元素,最后剩下的即为最大的k个元素,堆顶为第k个最大元素(求第k个最小元素就建立大顶堆)。

Python heapq库:

提供小顶堆的构建和一些对小顶堆的基本操作方法(如入堆,出堆等),可以用于实现堆排序算法(大顶堆可以取相反数来实现)。

创建堆的方法:

import heapq

  1. heappush(heap, num),先创建一个空堆,然后将数据一个一个地添加到堆中。每添加一个数据后,heap都满足小顶堆的特性。

  2. heapify(array),直接将数据列表调整成一个小顶堆。

常用方法:

heappop(heap), heappush(heap, num), nlargest(n, heap), nsmallest(n, heap), merge(list1, list2)

使用heapq实现堆排序:

  1. array = [10, 17, 50, 7, 30, 24, 27, 45, 15, 5, 36, 21]
  2. # 使用方法1创建堆
  3. heap = []
  4. for num in array:
  5. heappush(heap, num)
  6. heap_sort = [heappop(heap) for i in range(len(heap))]
  7. print("heap sort result: ", heap_sort)

获取堆中top-k最大或最小值:

  1. array = [10, 17, 50, 7, 30, 24, 27, 45, 15, 5, 36, 21]
  2. # 使用方法2创建堆
  3. heapify(array)
  4. print(nlargest(2, array))
  5. print(nsmallest(3, array))

归并两个有序列表:

  1. array_a = [10, 7, 15, 8]
  2. array_b = [17, 3, 8, 20, 13]
  3. array_merge = merge(sorted(array_a), sorted(array_b)) # 返回迭代器
  4. print("merge result:", list(array_merge))

其他方法:

heappushpop(heap, num):先push num,再pop堆顶元素。

heapreplace(heap, num):先pop堆顶元素,再push num。

3 桶

使用桶解决出现频率最多的k个元素问题。设置若干个桶,每个桶存储出现频率相同的数。桶的下标表示数出现的频率,即第i个桶中存储的数出现的频率为i。当把数都放入桶后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。

一般需要先使用dict记录数字出现的频率,再根据频率放入对应频率的桶里。如果可以有相同频率的数,那么桶就是一个二维数组。

  1. # 题目:给定数组nums和k,输出nums中出现频率前k高的元素。(力扣347)
  2. class Solution:
  3. def topKFrequent(self, nums: List[int], k: int) -> List[int]:
  4. freq = {} # dict记录数字出现频率
  5. for i in nums:
  6. if i in freq: freq[i] += 1
  7. else: freq[i] = 1
  8. bucket = [None] * (len(nums) + 1) # 桶序号代表频率,每个桶保存相同频率的数组成的列表
  9. for i in freq.keys():
  10. if not bucket[freq[i]]:
  11. bucket[freq[i]] = []
  12. bucket[freq[i]].append(i)
  13. ans = []
  14. for i in range(len(bucket) - 1, -1, -1): # 从后往前输出k个数
  15. if not bucket[i]: continue
  16. ans.extend(bucket[i])
  17. if len(ans) == k: return ans
  18. return ans

参考:

Leetcode 题解 - 排序 | CS-Notes

力扣

、Python heapq库的用法介绍_小斌哥ge的博客-CSDN博客_heapq pytho

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/840879?site
推荐阅读
相关标签
  

闽ICP备14008679号