求一个数组中第k小的数

1. 题目:给定一个无序整数数组,返回这个数组中第k小的数。

2. 分析:最平常的思路是将数组排序,最快的排序是快排,然后返回已排序数组的第k个数,算法时间复杂度为O(nlogn),空间复杂度为O(1)。使用快排的思想,但是每次只对patition之后的数组的一半递归,这样可以将时间复杂度将为O(n)。

  具体的思路:将数组按照第一个数字first进行划分,将比first小的放在左边,比first大的放在右边,first放中间。返回patition之后first的下标j。如果此时j+1==k(+1是因为数组下标从0开始)那么说明a[j]就是要找的第k个数。如果j+1<k,递归查找左半部分;如果j+1>k,递归查找右半部分。代码如下:

 1 #include <stdio.h>
 2 #include <iostream>
 3 #include <string>
 4 #include <cassert>
 5 #include <fstream>
 6 #include <cmath>
 7 using namespace std;
 8 
 9 int patition(int* num, int start,int end)
10 {
11     int p=num[start];
12     int i=start;
13     int j=end+1;
14     while (true)
15     {
16         while (num[++i]<=p && i<=end)
17         {
18         }
19         while (num[--j]>p && j>=start)
20         {
21         }
22         if (i>=j)
23         {
24             break;
25         }
26         swap(num[i],num[j]);
27     }
28     num[start]=num[j];
29     num[j]=p;
30 
31     for (int i=start;i<=end;i++)
32     {
33         cout<<num[i]<<"  ";
34     }
35     cout<<endl;
36     return j;
37 }
38 
39 //求一个无序数组的第k小的数字
40 int findKthSmall(int* num, int start,int end,int k)
41 {
42     int length=end-start+1;
43     assert(num && length>0 && k>0 && k<=end+1);
44     int j=patition(num,start,end);
45     if (k==(j+1))
46     {
47         return num[j];
48     }
49     else if (k<(j+1))
50     {
51         return findKthSmall(num,start,j-1,k);
52     }
53     else
54     {
55         return findKthSmall(num,j+1,end,k);
56     }
57 }
58 
59 int main()
60 {
61     enum{aLength=8};
62     int a[aLength]={12,4,8,8,8,1,15,9};
63     for (int i=0;i<aLength;i++)
64     {
65         cout<<"The "<<i+1<<"th small number is :"<<findKthSmall(a,0,aLength-1,i+1)<<endl;
66     }
67     return 0;
68 }

复杂度分析:平均时间复杂度O(n),这个见文章分析《递归式复杂度的计算》。

上面两种算法都是要改变原数组结构的(或者将原数组拷贝一份),下面的算法不需要改变原数组的结构。

思路:维持一个大小为k的大顶堆,遍历一次数组,如果数组中的元素比堆顶的元素小,那么就更新堆。最后堆中存放的是数组中的前k小元素。堆顶元素即为要求的第k小个数。

分析:这种算法不需要改变原数组结构,但是需要额外维持一个大小为O(k)的堆,时间复杂度为(nlogk)。当k比n小的多的时候,这个算法也是一个很好的选择。

转载于:https://www.cnblogs.com/ZJUKasuosuo/archive/2012/09/01/2666347.html

相关推荐

16、树莓派Pico W:蓝牙与Wi-Fi应用指南

本文介绍了树莓派Pico W在蓝牙与Wi-Fi通信方面的应用,涵盖蓝牙配对原理、使用HC-06模块实现手机远程控制LED和传输内部温度,以及通过Wi-Fi进行网络连接、扫描和UDP协议下的设备控制。文章包含详细电路连接、代码实现与测试步骤,并提供拓展建议、常见问题解决方法及开发注意事项,帮助读者快速掌握基于Pico W的无线通信项目开发,适用于物联网入门与实践场景。

sun99的博客 268

进阶版:找出数组中第k小的

常用的有三种方法: (假定数组下标从1开始存储据) 第一种方法: 先对数组进行升序排序,再直接输出L(k)。最容易想到的也是最容易实现的,不过也是平均时间复杂度最大的,为O(nlog2n)。 第二种方法: 利用小根堆的特性,先线性时间内建立小根堆,再不断调整每次堆顶元素都是剩下序列中的最小元素。整体实现不算麻烦,平均时间复杂度为O(n+klog2n)。 第三种方法(好多文章没讲清楚具体怎样递归子区间): 从数组L[1…n]中选择枢轴pivot(或者随便选一个)进行和快速排序一样的划分操作后,表L[1…n]

qq_45980351的博客 775

基于mengzi-t5训练生成式模型

【代码】基于mengzi-t5训练生成式模型。

开卷有益 143

如何找出数组中第k个最小的

方法一:排序法 最容易想到的方法就是对数组进行排序, 排序后的数组中第k-1个位置上的字即为 组的第k个最小的(原因是数组下标从0 开始计),这种方法最好的时间复杂度为 O(nlogn)。 方法二:“剪枝”法 找一个标签,比标签小的都放左边,比标签大的都放右边,如果标签最终的位置为k-1,则找到了,如果位置小于k-1,右半部分继续用标签来找,否则左半部分用标签来找,递归下去。 publi...

Mason97的博客 2611

一个数组里面的第k小元素(Acwing)

1、输入一个数组数组里有n个第k 小元素(第k小个) 2、先将数组进行快速排序 3、排序后划分两段,左段的元素个为s1个,将s1与k进行比较大小,如果k 小于这个s1,则说明这个第k小元素是左段,在左端找到第k个元素即可。 4、如果在右端,即k大于s1,这个元素就是第k-s1个(k的范围在这个数组内的各个地方均可) #include<iostream> using namespace std; int n, k;//输入的个为n,第k小的元素 const int N

m0_49317668的博客 2309

算法:给定一个数组找出其中最小的K个

题目描述 给定一个数组找出其中最小的K个。例如数组元素是4,5,1,6,2,7,3,8这8个字,则最小的4个字是1,2,3,4。如果K>数组的长度,那么返回一个空的数组 示例1 输入 [4,5,1,6,2,7,3,8],4 返回值 [1,2,3,4] import java.util.ArrayList; import java.util.List; public class Solution { public ArrayList<Integer> GetLeastNum

小达的博客 2114

C++ 查找数组中第k小的

/** 题目描述 给定一个数组a[0,...,n-1],数组中第k小 输入描述 首先输入数组长度n和k,其中1<=n<=5000, 1<=k<=n 然后输出n个整形元素,每个的范围[1, 5000] 输出描述 该数组中第k小 样例输入 4 2 1 2 3 4 样例输出 2 **/ #include<iostream> using names...

WYXHAHAHA123的博客 5505

一个数组中第K小的

面试南大夏令营的同学说被问到了这个问题,我的第一反应是建小顶堆,但是据他说用的是快排的方法说是O(n)的时间复杂度, 但是后来经过我的考证,这个算法在最坏的情况下是O(n^2)的,但是使用堆在一般情况下是O(n+klogn),最坏的情况是O(nlogn) 把两种方法整理一下,我还是推荐使用小顶堆的方法,因为它不仅可以第K大,还可以前K大。。。 一。快排。借用了快排的partit...

dcs2357181的博客 364

一个数组中前K大的或者第K大的

#include using namespace std; void BubbleSort(int a[], int alen) { for(int i = 0; i < alen; i++) { for(int j = alen-1; j > i; j--) { if(a[j] > a[j-1])

紫天火雨 3135

一个数组的第K大的

一个数组的第K大的

-lim-的博客 824

一个排序数组一个K出现的次

思路: 1 因为数组是有序的,因此可以遍历数组,遇到比K大的就终止,时间复杂度O(n) int GetNumberOfK(vector data ,int k) { int count=0; for(int i=0;i<data.size();i++){ if(data[i]==k){ cou

i_am_bird的博客 746

一个数组中第k大的

一个数组中第k大的 【方法一】冒泡: 因为只要冒泡k趟即可,第一趟冒泡第一大,第二次冒泡第二大,第k次冒泡第k大,时间复杂度为O(kn),n为数组长度。但是我们都知道快速排序是对冒泡的改进,降低冒泡的递归深度,使时间复杂度降低到O(nlgn),为什么不用快排呢?那么快排的时间复杂度又是多少呢? 【方法二】快排--不用完全快排完成: 因为快排每次将数组划分为两组加一个枢纽

suibianshen2012的专栏 1994

一个数组中第k大的方法

一个数组中第k大的,我第一印象是冒泡,因为只要冒泡k趟即可,第一趟冒泡第一大,第二次冒泡第二大,第k次冒泡第k大,时间复杂度为O(kn),n为数组长度。但是我们都知道快速排序是对冒泡的改进,降低冒泡的递归深度,使时间复杂度降低到O(nlgn),为什么不用快排呢?那么快排的时间复杂度又是多少呢? 因为快排每次将数组划分为两组加一个枢纽元素,每一趟划分你只需要将k与枢纽元素的下标进行比较,如

samjustin的博客 2203

一个有序整数组中和为K的的对

一个有序整数组中和为K的的对。() 输入 :int[] arr = {1, 1, 2, 3, 4, 5, 7, 7, 8}; K=8 输出:5 思路: 解决方案: 两个指针,一个在头,一个在尾; 大则-,小则加。 /** * @author: xjb * @date: 2020/6/8 * @description: **/ public class Test{ public static void main(String[] args) { int[] arr

未来的那个你 631

Leetcode-215:一个数组中第k大的

题目描述 给定一个未排序的整数组,找到其中第k大的 样例: 给出数组[4,5,1,2,3]和k=3,返回3; 给出数组[7,9,4,5]和k=1,返回9. 要时间复杂度为O(n),空间复杂度为O(1) 分析 快排–不用完全快排完成: 因为快排每次将数组划分为两组加一个枢纽元素,每一趟划分你只需要将k与枢纽元素的下标进行比较,如果比枢纽元素下标大就从右边的子数组中找,如果比枢...

Never-Giveup的博客 4898

一个有序整数组中和为K的的对

题目: 一个有序整数组中和为K的的对。 解决方案: 两个指针,一个在头,一个在尾; 大则-,小则加。   延伸题目: (1)数组中和为K的对。 先排序,O(N*logN),在按照以上算法查找O(N)。 (2)一个数组差为K的的对。 先排序,O(N*logN),然后,用两个指针均从头部开始,一个先走一个后走,差过小则前指针++,差过大则后指针++。 这

WangIcter的专栏 2849

java实现 一个数组中只有一个仅出现了一次,剩下的都出现了K次,只出现了一次的那个

此题我用的是位运算解,实际上也可以用哈希表,构成键值对解~ 步骤都在注释里,有问题欢迎留言交流 忽略类名…实在想不到啥英语 public class JinZhiMiaoYong { public static void main(String[] args) { /** * 思路: * 1)将数组中的每一个转换为K进制 * 2)逐个相加。因为是不进位加法,所以需要...

菜鸟的进阶之路 631
上一篇: 字典树(TrieTree)
下一篇: 错排问题
ah7975
博客等级 码龄10年 2粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值