hello,大家好 ,好久不见,甚是想念哇!
今天给大家分享一个TOPK问题,不过我这里不考虑特别大分布式的解决方案,普通的一道算法题。
首先搞清楚,什么是topK问题?
topK问题,就是找出序列中前k大(或小)的数,topK问题和第K大(或小)的解题思路其实大致一致的。
TopK问题是一个非常经典的问题,在笔试和面试中出现的频率都非常非常高(从不说假话)。下面,从小小白的出发点,认为topK是求前K大的问题,一起认识下TopK吧!
当前,在求TopK和第K大问题解法差不多,这里就用力扣215数组的第k个大元素 作为解答的题演示啦。可以看看这篇[程序员必知必会十大排序] 非常有助于学习!
找到TopK,并且排序TopK
啥,你想要我找到TopK?不光光TopK,你想要多少个,我给你多少个,并且还给你排序给排好,啥排序我最熟悉呢?
如果你想到冒泡排序O(n^2)那你就大意了啊。
如果使用O(n^2)级别的排序算法,那也是要优化的,其中冒泡排序和简单选择排序,每一趟都能顺序确定一个最大(最小)的值,所以不需要把所有的数据都排序出来,只需要执行K次就行啦,所以这种算法的时间复杂度也是O(nk)。
这里给大家回顾一下冒泡排序和简单选择排序区别:
冒泡排序和简单选择排序都是多趟,每趟都能确定一个最大或者最小,区别就是冒泡在枚举过程中只和自己后面比较,如果比后面大那么就交换;而简单选择是每次标记一个最大或者最小的数和位置,然后用这一趟的最后一个位置数和它交换(每一趟确定一个数枚举范围都慢慢变小)。
下面用一张图表示过程:
这里把code也给大家提供一下,简单选择上面图给的是每次选最小,实现的时候每次选最大就可以了。
//交换数组中两位置元素
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//冒泡排序实现
public int findKthLargest1(int[] nums, int k) {
for(int i=nums.length-1;i>=nums.length-k;i--)//这里也只是k次
{
for(int j=0;j<i;j++)
{
if(nums[j]>nums[j+1])//和右侧邻居比较
{
swap(nums,j,j+1);
}
}
}
return nums[nums.length-k];
}
//简单选择实现
public int findKthLargest2(int[] nums, int k) {
for (int i = 0; i < k; i++) {//这里只需要K次
int max = i; // 最小位置
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] > nums[max]) {
max = j; // 更换最小位置
}
}
if (max != i) {
swap(nums, i, max); // 与第i个位置进行交换
}
}
return nums[k-1];
}
当然,快排和归并排序甚至堆排序也可以啊,这些排序的时间复杂度为O(nlogn),也就是将所有数据排序完然后直接返回结果,这部分就不再详细讲解啦,调调api或者手写排序都可。
两种思路的话除了K极小的情况O(nk)快一些,大部分情况其实还是O(nlogn)情况快一些的,不过从O(n^2)想到O(nk),还是有所收获的。
这里需要知道堆相关的知识,我以前写过优先队列和堆排序,这里先不重复讲,大家也可以看一下:
[优先队列不知道,看看堆排序吧]
[硬核,手写一个优先队列]
上面说道堆排序O(nlogn)那是将所有元素都排序完然后取前k个,但是其实上我们分析一下这个堆排序的过程和几个注意点哈:
堆这种数据结构,分为大根堆和小根堆,小根堆是父节点值小于子节点值,大根堆是父节点的值大于子节点的值,这里肯定是要采用大根堆的。
堆看起来是一个树形结构,但是堆是个完全二叉树我们用数组存储效率非常高,并且也非常容易利用下标直接找到父子节点,所以都用数组来实现堆,每次排序完成的节点都将数移到数组末尾让一个新数组组成一个新的堆继续。
堆排序从大的来看可以分成两个部分,无序数组建堆和在堆基础上每次取对顶排序。其中无序数组建堆的时间复杂度为O(n),在堆基础上排序每次取堆顶元素,然后将最后一个元素移到堆顶进行调整堆,每次只需要O(logn)级别的时间复杂度,完整排序完n次就是O(nlogn),但是咱们每次只需要k次,所以完成k个元素排序功能需要花费O(klogn)时间复杂度,整个时间复杂度为O(n+klogn)因为和前面区分一下就不合并了。
画了一张图帮助大家理解,进行两次就获得Top2,进行k次就获得TopK了。
实现代码为:
class Solution {
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//下移交换 把当前节点有效变换成一个堆(大根)
public void shiftDown(int arr[],int index,int len)//0 号位置不用
{
int leftchild=index*2+1;//左孩子
int rightchild=index*2+2;//右孩子
if(leftchild>=len)
return;
else if(rightchild<len&&arr[rightchild]>arr[index]&&arr[rightchild]>arr[leftchild])//右孩子在范围内并且应该交换
{
swap(arr, index, rightchild);//交换节点值
shiftDown(arr, rightchild, len);//可能会对孩子节点的堆有影响,向下重构
}
else if(arr[leftchild]>arr[index])//交换左孩子
{
swap(arr, index, leftchild);
shiftDown(arr, leftchild, len);
}
}
//将数组创建成堆
public void creatHeap(int arr[])
{
for(int i=arr.length/2;i>=0;i--)
{
shiftDown(arr, i,arr.length);
}
}
public int findKthLargest(int nums[],int k)
{
//step1建堆
creatHeap(nums);
//step2 进行k次取值建堆,每次取堆顶元素放到末尾
for(int i=0;i<k;i++)
{
int team=nums[0];
nums[0]=nums[nums.length-1-i];//删除堆顶元素,将末尾元素放到堆顶
nums[nums.length-1-i]=team;
shiftDown(nums, 0, nums.length-i-1);//将这个堆调整为合法的大根堆,注意(逻辑上的)长度有变化
}
return nums[nums.length-k];
}
}
上面堆排序都能优化,那么快排呢?
快排当然能啊,这么牛的事情怎么能少得了我快排呢?
这部分需要堆快排有一定了解和认识,前面很久前写过:[图解手撕冒泡和快排] (后面待优化),快排的核心思想就是:分治 ,每次确定一个数字的位置,然后将数字分成两个部分,左侧比它小,右侧比它大,然后递归调用这个过程。每次调整的时间复杂度为O(n),平均次数为logn次,所以平均时间复杂度为O(nlogn)。
但是这个和求TopK有什么关系呢?
我们求TopK,其实就是求比目标数字大的K个,我们随机选一个数字例如上面的5,5的左侧有4个,右侧有4个,可能会出现下面几种情况了:
① 如果k-1等于5右侧数量,那么说明中间这个5就是第K个,它和它的右侧都是TopK。
②如果k-1小于5右侧数的数量 ,那么说明TopK全在5的右侧,那么可以直接压缩空间成右侧继续递归调用同样方法查找。
③ 如果k-1大于5右侧的数量,那么说明右侧和5全部在TopK中,然后左侧还有(k-包括5右侧数总数),此时搜查范围压缩,k也压缩。举个例子,如果k=7 那么5和5右侧已经占了5个数字一定在Top7中,我们只需要在5左侧找到Top2就行啦。
这样一来每次数值都会被压缩,这里因为快排不是完全递归,时间复杂度不是O(nlogn)而是O(n)级别(详细的可以找一些网上证明),但是测试样例有些极端代码比如给你跟你有序1 2 3 4 5 6…… 找Top1 就出现比较极端的情况。所以具体时候会用一个随机数和第一个交换一下防止特殊样例(仅仅为了刷题用的),当然我这里为了就不加随机交换的啦,并且如果这里要得到的TopK是未排序的。
详细逻辑可以看下实现代码为:
class Solution {
public int findKthLargest(int[] nums, int k) {
quickSort(nums,0,nums.length-1,k);
return nums[nums.length-k];
}
private void quickSort(int[] nums,int start,int end,int k) {
if(start>end)
return;
int left=start;
int right=end;
int number=nums[start];
while (left<right){
while (number<=nums[right]&&left<right){
right--;
}
nums[left]=nums[right];
while (number>=nums[left]&&left<right){
left++;
}
nums[right]=nums[left];
}
nums[left]=number;
int num=end-left+1;
if(num==k)//找到k就终止
return;
if(num>k){
quickSort(nums,left+1,end,k);
}else {
quickSort(nums,start,left-1,k-num);
}
}
}
排序总有一些骚操作的排序—线性排序,那么你可能会问桶类排序可以嘛?
也可以啦,不过要看数值范围进行优化,桶类排序适合数据均匀密集出现次数比较多的情况,而计数排序更是希望数值能够小一点。
那么利用桶类排序的具体核心思想是怎么样的呢?
先用计数排序统计各个数字出现次数,然后将新开一个数组从后往前叠加求和计算。
这种情况非常适合数值巨量并且分布范围不大的情况。
代码本来不想写了,但是念在你会给我三连我写一下吧
//力扣215
//1 <= k <= nums.length <= 104
//-104 <= nums[i] <= 104
public int findKthLargest(int nums[],int k)
{
int arr[]=new int[20001];
int sum[]=new int[20001];
for(int num:nums){
arr[num+10000]++;
}
for(int i=20000-1;i>=0;i--){
sum[i]+=sum[i+1]+arr[i];
if(sum[i]>=k)
return i-10000;
}
return 0;
}
好啦,今天的TopK问题就到这里啦,相信你下次遇到肯定会拿捏它。
TopK问题不难,就是巧妙利用排序而已。排序是非常重要的,面试会非常高频。
这里我就不藏着掖着摊牌了,以面试官的角度会怎么引导你说TOPK问题。
狡猾的面试官:
嗯,我们来聊聊数据结构与算法,来讲讲排序吧,你应该接触过吧?讲出你最熟悉的三种排序方式,并讲解一下其中具体算法方式。
卑微的我:
bia la bia la bia la bia la……
如果你提到快排,桶排序说不定就让你用这个排序实现一下TopK问题,其他排序也可能,所以掌握好十大排序是非常必要的!
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/OufnJo2UJ6xCmnuq2WKeTg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。