【Leetcode 堆、快速选择、Top-K问题 BFPRT】数组中的第K个最大元素(215)
这道题很强大,引出了很多知识点
题目
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
解答
题目要求找出一个序列中第K大元素,可以很容易想到下面的解法:
1,给序列排序,取出倒数第K个。快速排序为 n·log(n)
2,小顶堆,维护一个大小为K的小顶堆,把序列元素大于堆顶的元素依次入堆,完了堆顶就是第K大。维护一个堆(插入O(1),删除log(n))的时间复杂度是log(n),此处为log(k),总的时间复杂度是n·log(k),空间复杂度为O(k)。
PS:大顶堆也可以
3,快速选择算法,随时选择一个基准, 然后进行快排的partation过程(将序列中小于基准的都放在它的左边,大于它的都放在右边),基准归位,此时基准已经在序列中排好序的位置;再判断要找的第 N - k 个元素与基准坐标的关系, 如果k正好等于基准位置,那么数组第k小的数就是基准,如果K小于基准坐标位置,则只递归左半部分,否则只递归右半部分。
如果是快速排序算法,会在这里递归地对两部分进行快速排序,时间复杂度为n·log(n),而在这里,由于知道要找的第 N - k 小的元素在哪部分中,我们不需要对两部分都做处理,这样就将平均时间复杂度下降到O(n)。
这种算法最好情况是每次基准都划分在了序列中间位置,时间复杂度为O(n);最坏情况是每次基准都划分在了边缘位置,时间复杂度为O(n^2)。第四种方法优化了基准的选取,用线性复杂度O(n)的时间就解决了问题。
4,BFPRT, BFPRT算法就是在基准上做文章,能够保证每次所选的基准在数组的中间位置,那么时间复杂度就是O(N),BFPRT解法和快速选择解法唯一不同的就是在基准的选取上,所以只讲选取基准这一过程。
第一步:我们将数组每5个相邻的数分成一组,后面的数如果不够5个数也分成一组。
第二步:对于每组数,我们找出这5个数的中位数,将所有组的中位数构成一个median数组(中位数数组)。
第三步:我们再求这个中位数数组中的中位数,此时所求出的中位数就是基准。
第四步:通过这个基准进行partation过程,下面和常规解法就一样了。
BFPRT是专门用来求 TOP-K 问题的, 时间复杂度为O(N)。
通过代码如下:
1,排序
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
return sorted(nums)[-k]
2,小顶堆
from heapq import *
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
l = [] # 存储堆
for x in nums:
if l and len(l)==k and x>l[0]: # 堆满并且x大于堆顶,pop堆顶,x入堆
heapreplace(l, x)
if not l or len(l)<k: # 堆没满,直接入堆
heappush(l, x)
return l[0]
# 或者直接return nlargest(k, nums)[-1]
3,快速选择
import random
class Solution:
def findKthLargest(self, nums, k):
def partition(left, right, base):
temp = nums[base]
nums[base], nums[right] = nums[right], nums[base] # 基准和末尾元素互换
max_index = left
for i in range(left, right): # 把所有小于基准的移到左边
if nums[i] < temp:
nums[max_index], nums[i] = nums[i], nums[max_index]
max_index += 1
nums[right], nums[max_index] = nums[max_index], nums[right] # 基准归位
return max_index
def select(left, right, k_smallest):
"""在 nums[left, right] 找第k小的元素"""
if left == right: # 递归终止条件
return nums[left]
pivot_index = random.randint(left, right) # 随机选择基准(比固定选第一个要好)
base_index = partition(left, right, pivot_index) # 选第一个(left)为基准,并归位。
if base_index == k_smallest: # 判断目前已归位的基准,是不是第k_smallest位
return nums[k_smallest]
elif k_smallest < base_index: # go to 左半部分
return select(left, base_index - 1, k_smallest)
else: # go to 右半部分
return select(base_index + 1, right, k_smallest)
return select(0, len(nums) - 1, len(nums) - k) # 第k大,是第n-k小
4,BFPRT
class Solution:
def findKthLargest(self, nums, k):
def getmedian(lis):
"""返回序列lis中位数,在BFPRT中就是求每5个数小组的中位数"""
begin = 0
end = len(lis)-1
sum = begin+end
mid = sum//2 + sum % 2 # 这个地方加上sum%2是为了确保偶数个数时我们求的是中间两个数的后一个
return sorted(lis)[mid]
def BFPRT(nums, left, right):
"""分成每5个数一个小组,并求出每个小组内的中位数"""
num = right-left+1
offset = 0 if num % 5 == 0 else 1 # 最后如果剩余的数不足5个,我们也将其分成一个小组,和前面同等对待
groups = num//5 + offset
median = [] # 中位数数组
for i in range(groups):
begin = left+i*5
end = begin + 4
Median = getmedian(nums[begin:min(end, right)+1])
median.append(Median)
return getmedian(median)
def partition(nums, left, right, base):
"""在 nums[left, right] 将基准base归位"""
temp = nums[base]
nums[base], nums[right] = nums[right], nums[base] # 基准和末尾元素互换
max_index = left
for i in range(left, right): # 把所有小于基准的移到左边
if nums[i] <= temp: # 要等于啊!这里好坑的说.. 否则通不过样例[3, 3, 3, 3, 4, 3, 3, 3, 3] k = 1
nums[max_index], nums[i] = nums[i], nums[max_index]
max_index += 1
nums[right], nums[max_index] = nums[max_index], nums[right] # 基准归位
return max_index
def select(nums, left, right, k_smallest):
"""在 nums[left, right] 找第k小的元素"""
if left == right: # 递归终止条件
return nums[left]
# pivot_index = random.randint(left, right)
base = BFPRT(nums, left, right)
base_index = partition(nums, left, right, nums.index(base)) # 选base为基准,并归位。
if base_index == k_smallest: # 判断目前已归位的基准,是不是第k_smallest位
return nums[k_smallest]
elif k_smallest < base_index: # 递归左半部分
return select(nums, left, base_index - 1, k_smallest)
else: # 递归右半部分
return select(nums, base_index + 1, right, k_smallest)
return select(nums, 0, len(nums) - 1, len(nums) - k) # 第k大,是第n-k小
BFPRT笔记 | 对于求包含重复值的序列第K大,应该先去重再入BFPRT。
PS:大顶堆也可以,写都写出来了,不贴出来怪怪的..
from heapq import *
class Solution:
# 用负数入堆建立大顶堆,pop()k次,就是第k大。时间复杂度最坏n·log(n),最好k·log(n),空间复杂度O(n)
def findKthLargest(self, nums: List[int], k: int) -> int:
l = []
for x in nums:
heappush(l, -x) # 平均O(1),最坏log(n)
for x in range(k):
c = heappop(l) # log(n)
return -c
【Leetcode 堆、快速选择、Top-K问题 BFPRT】数组中的第K个最大元素(215)的更多相关文章
- 代码题(3)— 最小的k个数、数组中的第K个最大元素、前K个高频元素
1.题目:输入n个整数,找出其中最小的K个数. 例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4. 快排思路(掌握): class Solution { public ...
- [LeetCode]215. 数组中的第K个最大元素(堆)
题目 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 输出 ...
- 数组中的第K个最大元素leetcode(Top K的问题)
在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 输出: 5 ...
- LeetCode:数组中的第K个最大元素【215】
LeetCode:数组中的第K个最大元素[215] 题目描述 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: ...
- leetcode.排序.215数组中的第k个最大元素-Java
1. 具体题目 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 : 输入: [3,2,1,5,6,4] 和 k = ...
- Leetcode(215)-数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 输出: 5 ...
- LeetCode题解 | 215. 数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 输出: 5 ...
- LeetCode 数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 输出: 5 ...
- Leetcode题目215.数组中的第K个最大元素(中等)
题目描述: 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 ...
随机推荐
- Odoo中Qweb使用入门
参考 可参考官网例子https://doc.odoo.com/trunk/web/qweb/或 http://thierry-godin.developpez.com/openerp/tutorial ...
- 最小费用最大流——ZKW
对于最小费用最大流,我们的通常做法是EK+SPFA. 然而,卡常界大佬ZKW发明了一个求解最小费用最大流的方法,很强啊. 在学ZKW费用流前,先说说KM算法. KM算法 为啥要先提这个呢?因为ZKW费 ...
- wampserver配置服务
搭建服务器 windows下: 安装`WampServer`软件 1.什么是WampServer: WampServer,一般称之为 WAMP ,就是Windows Apache Mysql PHP集 ...
- windows下 将tomcat做成服务,并于oracle后启动
一.将tomcat做成服务 1.下载解压版的tomcat 6.*, 设置java.tomcat的环境(这个就不说了). 2.运行->cmd->到tomcat安装目录的bin目录: 3.运行 ...
- 深入浅出 Java Concurrency (2): 原子操作 part 1[转]
从相对简单的Atomic入手(java.util.concurrent是基于Queue的并发包,而Queue,很多情况下使用到了Atomic操作,因此首先从这里开始).很多情况下我们只是需要一个简单的 ...
- VirtualBox安装CentOS后分辨率和鼠标无缝切换问题
问题:VirtualBox安装完后出现分辨率只有800*600和1024*768,鼠标不能在虚拟机和本机件无缝切换. 解决办法:在终端中执行以下命令 yum install kernel yum in ...
- hibernate4一对多关联多方多写一次外键导致无法创建java.lang.NullPointerException以及Cannot add or update a child row: a foreign key constraint fails
一篇文章里边有多张图片,典型的单向一对多关系 多方 当程序运行到这一句的时候必然报错 但是参考书也是这样写的 其中em是 EntityManager em = JPA.createEntityMana ...
- Hibernate通用Dao
1. 接口 package com.coder163.main.dao; import org.hibernate.criterion.DetachedCriteria; import java.io ...
- nodejs+express 初学(一)
以下都是windows环境 1.下载nodejs http://www.nodejs.org/download/ 然后安装 2.确认已经安装完成 . node -v 3.安装express 注意: 是 ...
- JasperReports报表数据源10
数据源的结构数据容器.同时生成报告,Jasper报表引擎获得来自数据源的数据.数据可以从数据库,XML文件,对象数组和集合中的对象来获得.我们将在本章填充报告所看到的fillReportXXX()方法 ...