牛客算法竞赛入门班比赛1 第k小数(快排思想+读入优化)&快排解析

题目链接: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 t10,1n5×106,kn,数列里每个数都在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]<x时,将r[high]移至空单元r[low],此时r[high]相当于空单元。
(2)low从左向右扫描,直至r[low]>=x时,将r[low]移至空单元r[high],此时r[low]相当于空单元。

当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<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
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;
}

参考数据结构书上的快速排序算法和蓝桥杯试题:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
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)
{
	int x=a[low];///找一个基准 
	int i=low,j=high;
	while(i<j)
	{
		while(a[j]>=x&&i<j)
			j--;
		if(i<j)
			a[i]=a[j],i++;
		while(a[i]<x&&i<j)
			i++;
		if(i<j)
			a[j]=a[i],j--;
	}
	a[i]=x;
	if(i-low+1==k)
		return a[i];
	if(i-low+1<k)
		return finding(i+1,high,k-(i-low+1));
	else
		return finding(low,i-1,k);
}
int main()
{
	int t;
	t=read();
	while(t--)
	{
		n=read();
		k=read();
		for(int i=0; i<n; i++)
			a[i]=read();
		cout<<finding(0,n-1,k)<<endl;
	}
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值