牛客算法竞赛入门班比赛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] (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
#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;
}

你可能感兴趣的:(排序集锦,数据结构)