使用快速排序变化的第k个最小数字

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用快速排序变化的第k个最小数字相关的知识,希望对你有一定的参考价值。

我有以下分区方法和kthsmallest方法(快速排序的变化),它适用于某些情况,但在一些情况下给我值32767。

void swap(int* a, int* b){

int temp = *b;
*b = *a;
*a = temp;
}

int partition(int* arr, int l, int r){

int pivot = arr[r];
int i = l, j=0;

for(j=l; j<=r-1; j++){
    if(arr[j] <= pivot){
        swap(&arr[i], &arr[j]);
        i++;
    }
}

swap(&arr[i], &arr[j]);
return i;
}

kthsmallest函数如下: -

int kthsmallest(int* arr, int low, int high, int k){
 /* low = 0 and high = #elements - 1 */
 /* k is in between 1 to high + 1 */

 if (k>0 & k<=high-low+1)     {
    // pos is partitioning index, arr[p] is now  at right place
    int pos = partition(arr, low, high);

    // Separately sort elements before / partition and after partition

    if(pos-low == k-1){
            return arr[pos];
    }
    //if position returned is greater than k, recurse left subarray
    else if(pos-low > k-1){
            return kthsmallest(arr, low, pos-1, k);
    }

    return kthsmallest(arr, pos+1, high, k-(pos+1));

}
}

但是,当我在kthsmallest函数中更改最后一个调用时,它可以工作,即

变化:return kthsmallest(arr, pos+1, high, k-(pos+1));

致:return kthsmallest(arr, pos+1, high, k-(pos+1)+low);

我想了解为什么我需要将低值添加到k-(pos + 1)。因为在我看来,当我们在递归进入的右边有子阵列时,大数组中的第k个最小数字归结为k - 最后一个分区元素-1,即k-(pos + 1)。

答案

你需要low,因为当你递归地以一个新数组开始时,low将成为pos的参考。所以新的k将从low计算到pos

也许一个例子会更清楚。

让我们找到这个数组的第9个最小元素:array

现在我们进行分区,所以我们得到:partition1

pos到左边,我们得到了数组中最小的元素,这是3个最小的元素。现在我们将从pos+1开始使用子阵列。我们需要获得第六个最小元素:subarray1

我们对这个子阵列做了一个分区:partition2

请记住,我们正在研究一个试图找到第6个最小元素的子阵列。在这种情况下,我们将子阵列中的(pos - low + 1)th最小元素分开。所以我们新的k将是:subarray2我们做了一个新的分区:partition3现在我们超过了最后一个子阵列的第四个最小元素,所以我们修剪了最后一个部分:subarray3我们再次进行分区:partition4

我们得到:final

所以我们的号码是17

希望能帮助到你。

PD:正如David C. Rankin在评论中所说,你可能应该为&改变&&

另一答案

看来你遇到的一个问题就是当没有理由让函数递归时,试图将一个quickselect例程变成一个递归函数。您需要做的就是确定'k'元素所在的分区,不需要排序。你所需要的kthsmallest就是:

/** select the ZERO BASED 'k' element from 'arr'.
 *  where 'low' and 'high' are the ZERO BASED low
 *  and high indexes for 'arr'.
 */
int kthsmallest (int *arr, int low, int high, int k)
{
    for (;;) {
        if (low == high)
            return arr[low];

        int pos = partition (arr, low, high);

        if (k == pos)
            return arr[k];
        else if (k < pos)
            high = pos - 1;
        else
            low = pos + 1;
    }
}

使用精确的partitionswap函数,您可以编写一个小示例程序来测试数组中每个元素的k。 (注意:返回的元素基于零索引k,例如,第一个最小元素从数组末尾偏移零 - 就像在C的其余部分一样)

#include <stdio.h>

void swap (int *a, int *b);
int partition (int *arr, int l, int r);

/** select the ZERO BASED 'k' element from 'arr'.
 *  where 'low' and 'high' are the ZERO BASED low
 *  and high indexes for 'arr'.
 */
int kthsmallest (int *arr, int low, int high, int k)
{
    for (;;) {
        if (low == high)
            return arr[low];

        int pos = partition (arr, low, high);

        if (k == pos)
            return arr[k];
        else if (k < pos)
            high = pos - 1;
        else
            low = pos + 1;
    }
}

int main (void) {

    int a[] = { 51, 86, 34, 79, 92, 68, 14, 47, 22, 6 },
        nelem = sizeof a / sizeof *a;

    for (int i = 0; i < nelem; i++)
        printf (" nth (%2d) element is : %d
", i,
                kthsmallest (a, 0, nelem - 1, i));

    return 0;
}

void swap (int *a, int *b)
{
    int temp = *b;
    *b = *a;
    *a = temp;
}

int partition (int *arr, int l, int r)
{
    int pivot = arr[r];
    int i = l, j = 0;

    for (j = l; j <= r - 1; j++) {
        if (arr[j] <= pivot) {
            swap (&arr[i], &arr[j]);
            i++;
        }
    }

    swap (&arr[i], &arr[j]);

    return i;
}

示例使用/输出

$ ./bin/kthsmall
 nth ( 0) element is : 6
 nth ( 1) element is : 14
 nth ( 2) element is : 22
 nth ( 3) element is : 34
 nth ( 4) element is : 47
 nth ( 5) element is : 51
 nth ( 6) element is : 68
 nth ( 7) element is : 79
 nth ( 8) element is : 86
 nth ( 9) element is : 92

以上是关于使用快速排序变化的第k个最小数字的主要内容,如果未能解决你的问题,请参考以下文章

最小的k个数

29最小的K个数

最小的k个数

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

求最小的k个数

剑指offer:最小的K个数