归并排序和快速排序的衍生问题(二)

取数组中第n大的元素

取数组中第n大的元素这个问题,相信大家在学习数据结构这门课中都遇到过。通常我们会使用某一排序算法先将数组排序,然后在来找数组中第n大的元素。此时,数组中第n大的元素其在数组中的位置也为第n个。

利用这一思想,我们可以使用快速排序算法来将该求解算法的时间复杂度变为O(n)。为什么呢?在快速排序算法中,每一趟快速排序都会确定一个元素(基准)的位置,例如:假设现在基准为5,经过一趟快速排序后,我们知道在第五个元素(即该元素为5)之前的元素都小于5,在第五个元素之后的元素都大于5,这时我们不就确定了元素(基准)为5在数组中的位置吗?

我们先使用第一种方法来求解数组中第n大的元素,即先排序再找第n大的元素,其基本代码如下:

// 3-路快速排序算法
void __quickSort(int arr[], int l, int r) {

    if (l >= r)
        return;

    swap(arr[l], arr[rand() % (r - l + 1) + l]);
    int v = arr[l];

    // arr[l+1...lt] < v
    int lt = l;
    // arr[gt...r] > v
    int gt = r + 1;
    // arr[lt+1...i) == v
    int i = l + 1;

    while (i < gt) {
        if (arr[i] < v) {
            swap(arr[i], arr[lt + 1]);
            lt++;
            i++;
        } else if (arr[i] > v) {
            swap(arr[i], arr[gt - 1]);
            gt--;
        } else {
            i++;
        }
    }
    swap(arr[l], arr[lt]);

    __quickSort(arr, l, lt - 1);
    __quickSort(arr, gt, r);
}

int __selection(int arr[], int n, int k) {

    srand(time(NULL));
    __quickSort(arr, 0, n - 1);

    return arr[k];
}

// 寻找arr数组中第k大的元素
int selection(int arr[], int n, int k) {

    // 如果k >= 0 && k < n不成立,则终止程序执行
    assert(k >= 0 && k < n);

    return __selection(arr, n, k);
}

现在,我们来修改一下代码,使其成为第二种方法,其代码如下:

int __partition(int arr[], int l, int r) {

    swap(arr[l], arr[rand() % (r - l + 1) + l]);
    int v = arr[l];

    // arr[l+1...lt] < v
    int lt = l;
    // arr[gt...r] > v
    int gt = r + 1;
    // arr[lt+1...i) == v
    int i = l + 1;

    while (i < gt) {
        if (arr[i] < v) {
            swap(arr[i], arr[lt + 1]);
            lt++;
            i++;
        } else if (arr[i] > v) {
            swap(arr[i], arr[gt - 1]);
            gt--;
        } else {
            i++;
        }
    }
    swap(arr[l], arr[lt]);

    return lt;
}

int __selection(int arr[], int l, int r, 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);
}

// 寻找arr数组中第k大的元素
int selection(int arr[], int n, int k) {

    // 如果k >= 0 && k < n不成立,则终止程序执行
    assert(k >= 0 && k < n);
    srand(time(NULL));

    return __selection(arr, 0, n - 1, k);
}

这样,我们就解决了求取数组中第n个大元素的问题。

注:在main()中的代码如下:

int main() {

    // 生成一个大小为n, 包含0...n-1这n个元素的随机数组arr
    int n = 10000;
    int *arr = TestHelper::generateOrderedArray(n);
    TestHelper::shuffleArray(arr, n);

    // 验证selection算法, 对arr数组求第i小元素, 应该为i
    for (int i = 0; i < n; i++) {
        assert(selection(arr, n, i) == i);
        cout << "test " << i << " complete." << endl;
    }
    cout << "Test selection completed." << endl;

    delete[] arr;
    return 0;
}

由于测试方法为遍历输出,故不贴出程序运行结果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Ba la la la ~ 读者朋友们,你们好啊,又到了冷锋时间,话不多说,发车! 1.冒泡排序(Bub...
    王饱饱阅读 1,809评论 0 7
  • 一、 单项选择题(共71题) 对n个元素的序列进行冒泡排序时,最少的比较次数是( )。A. n ...
    貝影阅读 9,171评论 0 10
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到已排序好...
    依依玖玥阅读 1,270评论 0 2
  • 排序算法说明 (1)排序的定义:对一序列对象根据某个关键字进行排序; 输入:n个数:a1,a2,a3,…,an输出...
    BULL_DEBUG阅读 796评论 0 3
  • _豪杰_阅读 225评论 0 2