quickSelect算法返回第k个最小元素

时间:2015-10-19 04:03:54

标签: c algorithm sorting quickselect

我已按照quickSelect了解并实施了quickSelect算法。我不确定的一件事是:为什么他们会k-pivotpivot-first+1

虽然我的实现与此链接完全相似,但它无效。

#include <stdio.h>
#include <string.h>

#define DEBUG 1

#define debug(fmt, ...)\
    do{\
        if(DEBUG)\
            fprintf(stdout, "%s(%d) :   " fmt "\n", __FUNCTION__, __LINE__, __VA_ARGS__);\
    }while(0)

#define swap(a, b)\
    do{\
        if(a != b) {\
            a = a ^ b;\
            b = a ^ b;\
            a = a ^ b;\
        }\
    }while(0)


int
partition(int *a, int low, int high)
{
    int i = low, j = high;

    int pivot = a[i];
    i++;
    while(i < j)
    {
    while(pivot >= a[i])
        i++;
    while(pivot < a[j])
        j--;
    if(i < j)
        swap(a[i], a[j]);
    }

    swap(a[low], a[j]);
    return j;
}


int
quick_select(int *a, int start, int end, int k)
{
    if(start < end)
    {
    int pivot = partition(a, start, end);

    if(k < (pivot - start + 1))
       return quick_select(a, start, pivot, k);
    else if( k > (pivot - start + 1))
       return quick_select(a, pivot+1, end, k - pivot);
    else
    return a[pivot];
    }
}

int
main()
{
    int a[100], k, n;
    int ret, i;

    while(1)
    {
    printf("# of items  :   ");
    scanf("%d", &n);

    printf("Items   :   ");
    for(i = 0; i<n; i++)
        scanf("%d", &a[i]);

    printf("<k> :   ");
    scanf("%d", &k);

    ret = quick_select(a, 0, n-1, k);
    printf("[%d] smallest element = [%d]\n", k, ret);
    }

    return 0;
}

输出:

./a.out 
# of items  :   10
Items   :    1 2 3 4 5 6 7 8 9 10
<k> :   9
[9] smallest element = [32767]

2 个答案:

答案 0 :(得分:2)

首先,noman是对的。第二个quick_select是

quick_select(a, pivot+1, end, k - (pivot-start+1));

其次,要注意输入的含义。 startend在与当前递归调用相对应的原始列表中是绝对位置k是当前列表[start, end]

中的相对位置
  1. pivot-start+1是从pivot开始的当前子列表中索引start的相对位置。
  2. k - (pivot-start+1)(在您的alg k - pivot中)是以pivot开头的列表中k个最小元素的相对位置。例如,6 [1, 2, 3, 4, 5, 6]列表中的第4个最小元素是从3开始的子列表中的第2个最小元素[3, 4, 5, 6]

答案 1 :(得分:0)

只是你没有在一个案例中做pivot-1而你没有从K中减去正确的值来找到下一个K.

假设你有8个数字,你得到的数据为5.这意味着什么?这意味着如果您按非递减顺序排序并且小于第5个索引的所有数字小于该值,则第5个索引处的数字是5个最小元素。因此,如果您正在寻找7个最小的数字(我们称之为K),您应该在哪里搜索?你应该搜索6到8对吗?但是如果您仍然搜索6到8范围内的7个最小数字,那么K号应该会发生什么?没有权利?

你认为你不应该从7中减去6(0到5)个数字吗?如果你仍然认为不?然后阅读,否则停止阅读。

假设你有5个弟弟,你是所有兄弟中最高的。一个盲人来到你家,想要知道谁是你们所有人中第五高的。所以你告诉他说两个名字,你会告诉他谁是这些兄弟中最高的。这是他唯一可以要求找到第五高的问题。所以他所做的是与quicksort类似的东西。如果他选择你的第三个兄弟作为支点,并安排你的兄弟,他们的身高小于左边三分之一,右边另一个。在此之后,如果他计算你的第三个兄弟站在哪里,他就会知道你的第三个兄弟是第三高的在你的房子。现在他应该在哪里寻找第五高,显然是在右边?不是吗?

但他不知道第四高的位置在哪里。对?他所知道的是,第四和第五高是向右。所以你有两个人站在右边,他们两个人的身高都超过了第3,盲人想要知道第5个最高,但在这两个人之间,你应该找到第4个最高或第2个最高(5-3)在正确的组中(范围是4到5)?

我相信你现在已经理解了。

quick_select(int *a, int start, int end, int k)
{
    if(start < end)
    {
        int pivot = partition(a, start, end);

        if(k < (pivot - start + 1))
            return quick_select(a, start, pivot-1, k);
        else if( k > (pivot - start + 1))
            return quick_select(a, pivot+1, end, k - (pivot-start+1));
        else
            return a[pivot];
    }
}