快速选择算法的实现以找到第k个最小的数字

Posted

技术标签:

【中文标题】快速选择算法的实现以找到第k个最小的数字【英文标题】:Implementation of the quick select algorithm to find kth smallest number 【发布时间】:2016-03-23 11:27:09 【问题描述】:

我目前正在开发一个程序,以使用快速选择算法查找数组的第 k 个最小数。我已经完成了它,它可以工作,但每次都没有给出正确的结果。

这是我的代码(我没有包含我的 partitionswap 算法,我很确定它们是正确的):

/*
inputs...
*A: pointer to array
n: size of array
k: the item in question
*/
int ksmallest(int *A, int n, int k)

    int left = 0; 
    int right = n - 1; 
    int next = 1;

    return quickselect(A, left, right, k);


int quickselect(int *A, int left, int right, int k)

    //p is position of pivot in the partitioned array
    int p = partition(A, left, right);

    //k equals pivot got lucky
    if (p - 1 == k - 1)
        return A[p];
    
    //k less than pivot
    else if (k - 1 < p - 1)
        return quickselect(A, left, p - 1, k);
    
    //k greater than pivot
    else
        return quickselect(A, p + 1, right, k);
    

一切都编译得很好。然后我尝试在以下数组上使用该程序:[1,3,8,2,4,9,7]

这些是我的结果:

> kthsm 2
4
> kthsm 1
1
> kthsm 3
2

如您所见,它在第 1 个最小的项目上正常工作,但在其他项目上却失败了。可能是什么问题呢?我猜我的索引是关闭的,但我不确定。

编辑:根据要求在下面添加了我的分区和交换代码:

int partition(int *A, int left, int right)

    int pivot = A[right], i = left, x;

    for (x = left; x < right - 1; x++)
        if (A[x] <= pivot)
            swap(&A[i], &A[x]);
            i++;
        
    

    swap(&A[i], &A[right]);
    return i;


//Swaps
void swap(int *a, int *b)
    int temp = *a;
    *a = *b;
    *b = temp;

【问题讨论】:

尝试在调试器中逐行执行代码。对于这么小的数组,即使使用递归也不应该很难。 那么让我们看看 - 你有一个错误,你不知道它在哪里,而且你没有包含所有代码。嗯...你真的应该包含partitionswap 代码。 另外,您可以先尝试实现快速排序,然后考虑何时停止快速排序算法以获得您的快速选择解决方案。因为这基本上就是发生的事情。快速排序,只要达到大小为 k 的分区大小。 您确定要x &lt; right - 1 而不是x &lt; right 甚至x &lt;= right @WeatherVane 我改变了那行,但现在程序始终返回第 k+1 个最小的项目 【参考方案1】:

在您的分区函数中,循环条件应该是x &lt; right,而不是x &lt; right - 1

此外,在快速选择的 if 语句中,您应该将 p-1 的两种用法切换为 pp 已经是一个索引,通过将k 减 1,您也可以将其变成一个索引(而不是订单)。无需再将p 减一。

int partition(int *A, int left, int right)
    int pivot = A[right], i = left, x;
 
    for (x = left; x < right; x++)
        if (A[x] < pivot)
            swap(&A[i], &A[x]);
            i++;
        
    
 
    swap(&A[i], &A[right]);
    return i;

 
 
int quickselect(int *A, int left, int right, int k)
 
    //p is position of pivot in the partitioned array
    int p = partition(A, left, right);
 
    //k equals pivot got lucky
    if (p == k-1)
        return A[p];
    
    //k less than pivot
    else if (k - 1 < p)
        return quickselect(A, left, p - 1, k);
    
    //k greater than pivot
    else
        return quickselect(A, p + 1, right, k);
    

这是一个工作示例。 http://ideone.com/Bkaglb

【讨论】:

这会导致程序崩溃 @PythonNewb 添加了指向 ideone 的链接,因此您可以尝试使用它。当我运行它时,它似乎确实适用于 k 值 1 到 5。 确实可以,但是差1,第0个最小的项目返回1,当第一个最小的项目应该返回1时,对吗? @PythonNewb 我假设 k 是一个正整数。 (即我认为不可能有第 0 个最小的数字,但最小的数字实际上是第 1 个最小的数字)如果这个假设成立,那么这很好用。如果您打算在 [0, n-1] 范围内分配 k 的值,那么您只需将 if 语句中的两个 k-1 替换为 k,您就会得到您想要的。代码基本相同。您只需要区分 k 是索引还是指示元素顺序的值。 (即 0 索引或 1 索引) 我认为这并不重要,但 1965 年的原始实现使用 if (A[x] &lt; pivot)(而不是 &lt;=)。交换等于枢轴的值是没有意义的。

以上是关于快速选择算法的实现以找到第k个最小的数字的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode题解——算法思想之排序

重复大量输入的快速选择算法?

快速选择算法(求解第k大)

快速选择算法

算法 | 排序算法图形化比较:快速排序插入排序选择排序冒泡排序

排序算法(冒泡排序,选择排序,插入排序,快速排序)