牛客算法競賽入門班比賽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

t10,1n5×106,knt≤10,1≤n≤5×10^6 ,k≤n,數列裏每個數都在int範圍內

知識點

1、算法改進要點
冒泡排序掃描過程中只對相鄰的兩個元素進行比較,因此在互換兩個相鄰元素時只能消除一個逆序。
快速排序一次交換可能消除多個逆序。

2.算法思想
從待排序記錄序列中選取一個記錄(通常選取第一個記錄)爲樞軸,其關鍵字設爲K1K_1,然後將其餘關鍵字小於K1K_1的記錄移到前面,而將關鍵字大於或等於K1K_1的記錄移到後面,結果將待排序記錄序列分成兩個子表,最後將關鍵字爲K1K_1的記錄插到其分界線的位置處。將這個過程稱爲一趟快速排序

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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章