求数组中给定下标区间内的第 K 小元素
题目描述
给定数组,给定区间,求第K小的数。
伴随数组解法
我们知道,通常来讲,寻找给定区间内的第k小的元素的问题是ACM中一类常用的数据结构的一个典型例题,即划分树/逆向归并树,通常用线段树的结构存储。
当然这里暂且不表,尚不说划分树思想的神奇,就是线段树的结构,一般没有ACM基础的人也都觉得难以理解。所以,这里提供一个时间效率尚可,空间代价还要略小的巧妙解法—伴随数组。
首先,定义一个结构体,一个是数组元素,另一个是数组原来的标号,记录每个数在数组的原顺序。
我们以下面的测试数据举例(红体部分表示下标为2~5之间的数5,2,6,3,浅色部分表示数组中的数各自对应的数组下标,淡蓝色部分为给定的下标区间,注,这里,我们让数组下标从1开始):
现在,题目给定了下标区间,如在原序列中下标2~5(即下标为2,3,4,5
)区间找到第3小的数。问题亦相当于要你找原序列里给定下标区间即第2个数到第5个数之中(5,2,6,3
)第3小的数(当然,答案很明显,第3小的数就是5)。
那么对原数组进行排序,然后得到的序列应该是(注:原下标始终保持不变):
如上,既然数据现在已经从小到大排好了,那么,我们只需要进行一次检索,从最小的数到最大的数,我们找第k(k=3)小的数,当我们发现下标a[i].num
等于原给定下标区间在2~5中,即a[i].num==2 || 3 || 4 || 5
的时候,k--,那么当k==0的时候,我们也就找到了第k(3)小的数了。如下(红色部分表示原给定下标区间中的数,浅色部分依然是原各数对应的下标,淡蓝色部分为原来给定的下标区间所对应的索引):
故下标索引为2~5之间第k(3)小的数是5。
程序的构造与解释:由于排序后,我们能保证原序列已经从小到大的排好序了,所以,当遍历或扫描到原序列给定下标区间中的数时,则k--,最终能在k==0时,找到第k小的数,且这个数是在原来给定下标区间中的某一个数。
而这个伴随数组,或者说原序列各数的索引则帮我们或者说是帮电脑记下了原来的数,已让我们后来遍历时能识别排序后序列中的数是否是给定下标区间中的某一个数。如果是原给定下标区间中的数,则k--,否则k不变。
上述采用伴随数组的方法巧妙且简单,也很好理解和实现,关键 就是在于题目要求是在给定下标区间中找寻第k小(大)的元素,所以,基本上在排序n*logn
完了之后,总能 在O(n)
的时间内找到想找的数。源代码如下:
//:July:2_2.cpp
//求数组中给定下标区间内的第 K 小元素
//2014-05-31
//by lay
#include<iostream>
using namespace std;
typedef struct node
{
int data;
int num;
}Node;
int Partition(Node Seq[],int p,int r )
{
int i=p-1;
int j;
int temp1,temp2;
for(j=p;j<r;j++)
{
if(Seq[j].data<=Seq[r].data)
{
++i;
temp1=Seq[i].data;
temp2=Seq[i].num;
Seq[i].data=Seq[j].data;
Seq[i].num=Seq[j].num;
Seq[j].data=temp1;
Seq[j].num=temp2;
}
}
++i;
temp1=Seq[r].data;
temp2=Seq[r].num;
Seq[r].data=Seq[i].data;
Seq[r].num=Seq[i].num;
Seq[i].data=temp1;
Seq[i].num=temp2;
return i;
}
void QuickSort(Node Seq[],int p,int r )
{
int q;
if(p<r)
{
q=Partition(Seq, p,r );
Partition(Seq, p,q-1 );
Partition(Seq, q+1,r );
}
}
int FirstK(Node Seq[],int k,int n,int first,int last )
{
for(int j=0;j<n;j++)
{
if(Seq[j].num>=first&&Seq[j].num<=last)
k--;
if(k==0)
return Seq[j].data;
}
}
int main()
{
const int n=10;
int k=3;
Node Seq[n];//={20,45,26,45,12,54,12,63,48,35....,32,65,47,85,23,16,71,39,321,100};
cout<<"Please Initialize the sequence,the length is : "<<n<<endl;
for(int i=0;i<n;i++)
{
cin>>Seq[i].data;
Seq[i].num=i;
}
QuickSort(Seq,0,n-1);
int knum;
knum=FirstK(Seq,k,n,2,8 );
cout<<knum<<endl;
return 0;
}