寻找数组中第n大的元素

0x00 问题简述

给定一个数组,找出该数组中第n大的元素的值。其中,1<=n<=length。例如,给定一个数组A={2,3,6,5,7,9,8,1,4},当n=1时,返回9

0x01 先排序

我拿到这个问题的地中思路就是先排序,然后通过位置索引相应的第n大的元素。我使用的是O(nlog(n))级别的排序算法,所以这种方法的时间复杂度应该也是O(nlog(n))级别的。

那这个问题有没更好的解决办法呢?是有的,我们参考快速排序的思想,可以做到O(n)级别。在此之前我们先回顾一下快速排序。

0x02 快速排序

我这里只是简述以下快速排序的思路。快速排序中最重要的就是partition操作,就是将我们最左边的数k移动到最佳位置(k左边的数都小于kk右边的数都大于k



我们通过以下这个例子回顾以下具体操作方式





具体的partition操作代码

template<typename T>
int __partition(T arr[], int l, int r)
{
    std::swap(arr[l], arr[rand() % (r - l + 1) + l]);
    int j = l;
    for (int i = l + 1; i <= r; ++i)
    {
        if (arr[i] < arr[l])
        {
            std::swap(arr[++j], arr[i]);
        }
    }
    std::swap(arr[l], arr[j]);
    return j;
}

上面代码是最基础的操作。

0x03 O(n)级别的处理

我们通过快速排序中的思想,可以很快地解决这个问题

template<typename T>
int __partition(T arr[], int l, int r)
{
    std::swap(arr[l], arr[rand() % (r - l + 1) + l]);
    int j = l;
    for (int i = l + 1; i <= r; ++i)
    {
        if (arr[i] < arr[l])
        {
            std::swap(arr[i], arr[++j]);
        }
    }
    std::swap(arr[l], arr[j]);
    return j;
}
template<typename T>
int __selection(T arr[], const int& l, const int& r, const int& k)
{
    if (l == r) return arr[l];
    int p = __partition(arr, l, r);
    if (k == p) return arr[p];
    else if (k < p) return __selection(arr, l, p - 1, k);
    else return __selection(arr, p + 1, r, k);
}
template<typename T>
int selection(T arr[], const int& n, const int& k)
{
    assert(k >= 0 && k < n);
    srand(time(NULL));
    return __selection(arr, 0, n - 1, k);
}

我们回顾上面的做法,不难发现,我们每次对问题的规模缩小一半,算法复杂度为

n + n/2 + n/4 + ... + 1 = 2n,即为O(2n)

猜你喜欢

转载自blog.csdn.net/qq_17550379/article/details/80019784