0

我按照quickSelect来理解和实现 quickSelect 算法。我不确定的一件事是:他们为什么这样做k-pivot并且pivot-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]
4

2 回答 2

2

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

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

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

  1. pivot-start+1是当前子列表中索引的相对位置,pivot从 开始start
  2. k - (pivot-start+1)(在您的 alg 中k - pivot)是列表中以枢轴开头的 k 最小元素的相对位置。例如,6 个列表中的第 4 个最小元素[1, 2, 3, 4, 5, 6]是从 3 开始的子列表中的第 2个最小元素[3, 4, 5, 6]
于 2015-10-19T08:23:13.653 回答
0

只是在一种情况下你没有做 pivot-1 并且你没有从 K 中减去正确的值来找到下一个 K。

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

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

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

但他不知道第四高的站在右边的哪个位置。正确的?他只知道第 4 和第 5 高在右边。所以你有两个人站在右边,他们的身高都超过了第三,盲人想知道第五高,但是在这两个中,你应该在正确的组中寻找第四高还是第二高(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];
    }
}
于 2015-10-19T07:51:52.710 回答