随机选择算法---Randomized Selection Algorithm

Introduction

我们已经见过很多关于顺序统计的信息。在不排序的情况下,在一个无序数组中找到第K小的数,你将会怎么做呐?


找最大值一般的做法就是定义一个最大值变量,来遍历数组。但是如果要找的是第二大的数值又如何做呐?你是不是需要两个变量?如果是找第100小的数值呐?


解决上述问题的方法就是接下来我即将介绍的随机选择算法——Randomized Selection Algorithm


Concept

首先做随机对数组进行partition,得到一个随机点在数组中的位置,然后减去数组的开始位置,这个可能就是要找的数组中 k-th 位置

例如:

数组:arr = 9,5,7,1,10,2,3

如果随机选择的中间点为5,然后根据5进行partition,结果为:

数组:arr = 1 2 3 5 9 7 10

partition 之后返回的位置为pos = 3(数组开始位置为0),所以确切的第k个位置的计算公式为:

pos - start + 1 (pos是返回的位置)


如果上述公式计算的结果和k相等的话,说明我们已经获得最佳解,仅仅用了O(n)时间就找到了。


但是不可能我们随机选择的点就是我们要找的数值,所以在第一步partition之后还未找到应该如何做呐?

我们继续上述过程:

        1   2   3      5      9  7   10

      start          mid            end


现在  k = mid - start + 1 = 4

现在我们需要第6小的数值,我们眼睛看的话就是7,现在我们已经得到第4小的了。既然 4  < 6,所以我们可以肯定第6小的应该在右边,即:

在  9  7  10 这一边

数组这边的起始位置为mid + 1,

但是第6小的数值在这半边数组应该是第(6 - 4)小的数值。所以我们递归实现就可以找到了。


原理就这些,接下来就是源码:

#include 
#include 

using namespace std;

int random_partition(int* arr, int start, int end)
{
    int pivotIdx = start + rand() % (end-start+1);
    int pivot = arr[pivotIdx];
    swap(arr[pivotIdx], arr[end]); // move pivot element to the end
    pivotIdx = end;
    int i = start -1;

    for(int j=start; j<=end-1; j++)
    {
        if(arr[j] <= pivot)
        {
            i = i+1;
            swap(arr[i], arr[j]);
        }
    }

    swap(arr[i+1], arr[pivotIdx]);
    return i+1;
}

int random_selection(int* arr, int start, int end, int k)
{
    if(start == end)
        return arr[start];

    if(k ==0) return -1;

    if(start < end)
    {

        int mid = random_partition(arr, start, end);
        int i = mid - start + 1;
        if(i == k)
            return arr[mid];
        else if(k < i)
            return random_selection(arr, start, mid-1, k);
        else
            return random_selection(arr, mid+1, end, k-i);
    }

}
int main()
{
    int A[] = {9,5,7,1,10,2,3};
    int loc = random_selection(A, 0,6,5);
    cout << "the 5th smallest is  " << loc << endl;
    return 0;
}

你可能感兴趣的:(程序算法)