题目链接:https://ac.nowcoder.com/acm/contest/5773/A
题目描述
给你一个长度为n的序列,求序列中第k小数的多少。
输入描述:
多组输入,第一行读入一个整数T表示有T组数据。
每组数据占两行,第一行为两个整数n,k,表示数列长度和k。
第二行为n个用空格隔开的整数。
输出描述:
对于每组数据,输出它的第k小数是多少。
每组数据之间用空格隔开
输入
2
5 2
1 4 2 3 4
3 3
3 2 1
输出
2
3
t ≤ 10 , 1 ≤ n ≤ 5 × 1 0 6 , k ≤ n t≤10,1≤n≤5×10^6 ,k≤n t≤10,1≤n≤5×106,k≤n,数列里每个数都在int范围内
1、算法改进要点
冒泡排序扫描过程中只对相邻的两个元素进行比较,因此在互换两个相邻元素时只能消除一个逆序。
快速排序一次交换可能消除多个逆序。
2.算法思想
从待排序记录序列中选取一个记录(通常选取第一个记录)为枢轴,其关键字设为 K 1 K_1 K1,然后将其余关键字小于 K 1 K_1 K1的记录移到前面,而将关键字大于或等于 K 1 K_1 K1的记录移到后面,结果将待排序记录序列分成两个子表,最后将关键字为 K 1 K_1 K1的记录插到其分界线的位置处。将这个过程称为一趟快速排序
3.算法步骤
假设待划分序列为r[low],r[low+1],…,r[high]。首先将基准记录r[low]移至变量x中,使r[low]相当于空单元,然后反复进行如下两个扫描过程,直至low和high相遇。
(1)high从右向左扫描,直到r[high]
当low和high相遇时,r[low] (或r[high])相当于空单元,且r[low]左边所有记录的关键字均小于基准记录的关键字,而r[low]右边所有记录的关键字均大于或等于基准记录的关键字。最后将基准记录移至r[low]中,就完成了一次划分过程。
完整快速排序代码:
int QKPass(int a[],int low,int high)
{
int x=a[low];//选择基准记录
while(low<high)
{
while(low<high&&a[high]>=x)
high--;//high从右到左找小于x的记录
if(low<high)
{
a[low]=a[high];//找到小于x的记录,则送入空单元r[low]
low++;
}
while(low<high&&a[low]<x)
low++;//low从左到右找大于或等于x的记录
if(low<high)
{
a[high]=a[low];//找到大于或等于x的记录,则送入空单元r[high]
high--;
}
}
a[low]=x;//将基准保存在low=high的位置
return low;//返回基准的位置
}
void QKSort(int a[],int low,int high)
{
if(low<high)
{
int pos=QKPass(a,low,high);
QKSort(a,low,pos-1);//划分两个子表
QKSort(a,pos+1,high);
}
}
题目分析:
1.
在进行快速排序的时候在对左右子表进行排序,判断左子表元素的个数和k的关系。
例如:基准左边的数都比基准小,若基准左边的数的个数大于k,对于基准右边的数我们就不用管了。
int finding(int low,int high,int k)
{
if(low==high)
return a[low];
int i=low,j=high;
int mid=(low+high)>>1;
int x=a[mid];
while(i<=j)
{
while(a[i]<x)
i++;
while(a[j]>x)
j--;
if(i<=j)
{
swap(a[i],a[j]);
i++;
j--;
}
}
if(k<=j)
return finding(low,j,k);
else if(k>=i)
return finding(i,high,k);
else
return a[k];
}
2.快读操作
inline int read()
{
int x = 0, f = 1;///f判断正负
char ch = getchar();
while(ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x<<1) + (x<<3) + (ch^48);//相当于x=x*10+ch,位移操作提高效率
ch = getchar();
}
return x * f;
}
完整代码:
#include
#include
#include
#include
using namespace std;
const int N=5*1e6+10;
int n,k,a[N];
inline int read()
{
int x = 0, f = 1;///f判断正负
char ch = getchar();
while(ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x<<1) + (x<<3) + (ch^48);//相当于x=x*10+ch,位移操作提高效率
ch = getchar();
}
return x * f;
}
int finding(int low,int high,int k)
{
if(low==high)
return a[low];
int i=low,j=high;
int mid=(low+high)>>1;
int x=a[mid];
while(i<=j)
{
while(a[i]<x)
i++;
while(a[j]>x)
j--;
if(i<=j)
{
swap(a[i],a[j]);
i++;
j--;
}
}
if(k<=j)
return finding(low,j,k);
else if(k>=i)
return finding(i,high,k);
else
return a[k];
}
int main()
{
int t;
t=read();
while(t--)
{
n=read();
k=read();
for(int i=1; i<=n; i++)
a[i]=read();
cout<<finding(1,n,k)<<endl;
}
return 0;
}