求一个数组中第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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值