【问题标题】:kth smallest number using variation of quicksort使用快速排序变体的第 k 个最小数
【发布时间】:2016-08-15 03:12:59
【问题描述】:

我有以下分区方法和 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)。

【问题讨论】:

  • 如果 k 很小,你可以只保留一个子缓冲区,你可以在 ~n 时间内收集 k 个元素,其中快速排序总是 O(n log n)...我知道它没有t帮助你的问题
  • 我认为k&gt;0 &amp; k&lt;=high-low+1 是一个错字,而您想要&amp;&amp;?另外,这个快速选择例程是从哪里来的?这是你从零开始发明的吗?我问是因为看起来你需要把它带回绘图板上。请记住,您不需要排序,只需要识别包含第 k 个元素的分区。我怀疑缺乏进一步的评论或答案是由于很多人挠头试图弄清楚你想要完成的事情。请发帖Minimal, Complete, and Verifiable example
  • @DavidC.Rankin 快速选择?我没有在问题的任何地方引用它。如果您的意思是快速排序,那么我已经在问题本身中指定这是快速排序算法的一种变体。是的,我不需要排序,这就是分区基于“k”的原因。

标签: c sorting debugging


【解决方案1】:

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

也许举个例子会更清楚。

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

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

pos 到左边,我们有数组中最小的元素,也就是 3 个最小的元素。现在我们将使用从pos+1 开始的子数组。我们需要得到第 6 个最小的元素:

我们对这个子数组进行分区:

请记住,我们正在处理一个子数组,试图找到第 6 个最小的元素。在这种情况下,我们分离了子数组中的(pos - low + 1)th 最小元素。所以我们的新k 将是: 我们做一个新的分区: 现在我们超过了最后一个子数组的第 4 个最小元素,所以我们修剪最后一部分: 我们再次进行分区:

我们得到:

所以我们的号码是17

希望对你有帮助。

PD:正如 David C. Rankin 在 cmets 中所说,您可能应该将 &amp; 更改为 &amp;&amp;

【讨论】:

    【解决方案2】:

    您遇到的问题之一似乎是试图将快速选择例程硬塞到 recursive 函数中,而 没有理由 使函数开始递归.您需要做的就是识别'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,例如 first em> 最小元素是从数组末尾偏移 -- 就像在 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\n", 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
    

    【讨论】:

    • 当 pivot k - pivot 最小元素吗?
    • 好吧,我认为你正在让你的低点和高点彼此靠近,直到他们找到正确的位置,你会说这种方法更快或更慢还是大致与递归消除一些的想法相当部分并在这个新的子问题中寻找解决方案?
    猜你喜欢
    • 2021-01-06
    • 2017-06-23
    • 2016-02-22
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    • 2021-06-07
    • 2016-09-25
    • 2013-02-17
    相关资源
    最近更新 更多