题目

https://www.acwing.com/problem/content/788/

算法思想

利用快速排序的分治思想,在每次数组被分为两部分的时候,判断当前 k 属于左半边还是右半边,如果 k 属于左半边,则只针对左半边继续处理,直到最后收敛到 l >= r 的情况,即可以返回。

解法一

#include <iostream>

const int N = 100010;

int arr[N];

int quick_sort_k(int l, int r, int k) {
    if(l >= r) return arr[l];
    
    int x = arr[l+((r-l)>>1)], i = l - 1, j = r + 1;
    
    while(i < j) {
        do { ++i; } while(arr[i] < x);
        do { --j; } while(arr[j] > x);
        if(i < j) std::swap(arr[i], arr[j]);
    }
    
    if(j + 1 >= k) return quick_sort_k(l, j, k);
    
    return quick_sort_k(j + 1, r, k);
}

int main() {
    int n, k;
    std::cin >> n >> k;
    
    for(std::size_t i = 0; i < n; ++i)
        std::cin >> arr[i];
    
    std::cout << quick_sort_k(0, n - 1, k);
    
    return 0;
}

解法二

#include <iostream>

const int N = 100010;

int arr[N];

int quick_sort_k(int l, int r, int k) {
    if(l >= r) return arr[l];
    
    int i = l - 1, j = r + 1, x = arr[l+((r-l)>>1)];
    int s = l;
    
    while(s < j) {
        if(arr[s] < x) {
            std::swap(arr[s++], arr[++i]);
        } else if(arr[s] > x) {
            std::swap(arr[s], arr[--j]);
        } else {
            ++s;
        }
    }
    
    if((i < k-1) && (j > k-1)) return x;
    
    if(i + 1 >= k) return quick_sort_k(l, i, k);
    
    return quick_sort_k(j, r, k);
}

int main() {
    int n, k;
    std::cin >> n >> k;
    
    for(std::size_t i = 0; i < n; ++i)
        std::cin >> arr[i];
    
    std::cout << quick_sort_k(0, n - 1, k);
    
    return 0;
}

时间复杂度

第一次分区查找,需要针对 n 个元素进行处理,第二次需要针对 n/2 个元素,第三次是 n/4,直到缩小为 1,即

n + n/2 + n/4 + n/8 ... +1

这是等比数列求和,最后的和等于 2n - 1,所以时间复杂度为 O(n)。