bfprt算法原理和复杂度估算

来源:互联网 发布:用c语言编写的代码是 编辑:程序博客网 时间:2024/06/06 09:03

在文章开头先了解随机快速排序,随机排序之前博文中有,在此增加快速排序中partition函数,参照http://www.cnblogs.com/sdlwlxf/p/5131793.html


Master公式:用于算复杂度


此公式只适用于每次递归量相同的情况,可以简化理解为一下过程:

a表示递归的次数,b表示递归规程中每次递归缩小是的数据量,n^d为递归后续行为的复杂度。比如,数据量为n,归并排序时复杂度=前n/2的复杂度+后n/2的复杂度+将这两个n/2合并成一个数组时的复杂度。


bfprt算法原理:

在无序数组中,找到最小或最大第k个数的算法思想:

在快速排序或是随机快速排序中,步骤为:1、用速排序或是随机快速排序方法找X      2、partition函数把数组排成 >x  ==x  <x  3、看==x的下标分为是否包含k

若在即可得到第k个数,若不在,则在>x  或<x  中步骤1 开始重新查找,直至找到。此方法的平均复杂度为O(n),但单次复杂度不一定是。


bfprt算法只是在选择划分值的策略上不一样,其他的与快排一样。

bfprt算法的划分策略是:1、将数组每五个划为一组,不够5个单列一组     复杂度O(1)

                                            2、每个组中的数据进行组内排序         复杂度O(n)

                                            3、把每个组中的中位数拿出,组成一个新的数组   n/5    复杂度O(n)

                                            4、求出n/5这个数组的中位数,这个数在数组中是第n/10个数,用重复调用自己的方法来找。此处的中位数就是所要找的划分值 F(n/5)

                                            5、partition函数把数组排成 >x  ==x  <x    复杂度O(n)

                                            6、看==x的下标分为是否包含k若在即可得到第k个数,若不在,则在>x  或<x  中步骤1 开始重新查找,直至找到。


假设五组已经排好的序列,X出为最后找的中位数,在数组的n/10的位置上。<=这个值的有n*3/10,>这个值的为n*7/10

整个复杂度:F(n)=F(n/5)+F(n*7/5)+O(n)

JAVA程序:

package douyu_2017_07_31;


public class Problem_01_FindMinKNums {


// O(N*logK)
public static int[] getMinKNumsByHeap(int[] arr, int k) {
if (k < 1 || k > arr.length) {
return arr;
}
int[] kHeap = new int[k];
for (int i = 0; i != k; i++) {
heapInsert(kHeap, arr[i], i);
}
for (int i = k; i != arr.length; i++) {
if (arr[i] < kHeap[0]) {
kHeap[0] = arr[i];
heapify(kHeap, 0, k);
}
}
return kHeap;
}


public static void heapInsert(int[] arr, int value, int index) {
arr[index] = value;
while (index != 0) {
int parent = (index - 1) / 2;
if (arr[parent] < arr[index]) {
swap(arr, parent, index);
index = parent;
} else {
break;
}
}
}


public static void heapify(int[] arr, int index, int heapSize) {
int left = index * 2 + 1;
int right = index * 2 + 2;
int largest = index;
while (left < heapSize) {
if (arr[left] > arr[index]) {
largest = left;
}
if (right < heapSize && arr[right] > arr[largest]) {
largest = right;
}
if (largest != index) {
swap(arr, largest, index);
} else {
break;
}
index = largest;
left = index * 2 + 1;
right = index * 2 + 2;
}
}


// O(N)
public static int[] getMinKNumsByBFPRT(int[] arr, int k) {
if (k < 1 || k > arr.length) {
return arr;
}
int minKth = getMinKthByBFPRT(arr, k);
int[] res = new int[k];
int index = 0;
for (int i = 0; i != arr.length; i++) {
if (arr[i] < minKth) {
res[index++] = arr[i];
}
}
for (; index != res.length; index++) {
res[index] = minKth;
}
return res;
}


public static int getMinKthByBFPRT(int[] arr, int K) {
int[] copyArr = copyArray(arr);
return select(copyArr, 0, copyArr.length - 1, K - 1);
}


public static int[] copyArray(int[] arr) {
int[] res = new int[arr.length];
for (int i = 0; i != res.length; i++) {
res[i] = arr[i];
}
return res;
}


public static int select(int[] arr, int begin, int end, int i) {
if (begin == end) {
return arr[begin];
}
int pivot = medianOfMedians(arr, begin, end);
int[] pivotRange = partition(arr, begin, end, pivot);
if (i >= pivotRange[0] && i <= pivotRange[1]) {
return arr[i];
} else if (i < pivotRange[0]) {
return select(arr, begin, pivotRange[0] - 1, i);
} else {
return select(arr, pivotRange[1] + 1, end, i);
}
}


public static int medianOfMedians(int[] arr, int begin, int end) {
int num = end - begin + 1;
int offset = num % 5 == 0 ? 0 : 1;
int[] mArr = new int[num / 5 + offset];
for (int i = 0; i < mArr.length; i++) {
int beginI = begin + i * 5;
int endI = beginI + 4;
mArr[i] = getMedian(arr, beginI, Math.min(end, endI));
}
return select(mArr, 0, mArr.length - 1, mArr.length / 2);
}


public static int[] partition(int[] arr, int begin, int end, int pivotValue) {
int small = begin - 1;
int cur = begin;
int big = end + 1;
while (cur != big) {
if (arr[cur] < pivotValue) {
swap(arr, ++small, cur++);
} else if (arr[cur] > pivotValue) {
swap(arr, cur, --big);
} else {
cur++;
}
}
int[] range = new int[2];
range[0] = small + 1;
range[1] = big - 1;
return range;
}


public static int getMedian(int[] arr, int begin, int end) {
insertionSort(arr, begin, end);
int sum = end + begin;
int mid = (sum / 2) + (sum % 2);
return arr[mid];
}


public static void insertionSort(int[] arr, int begin, int end) {
for (int i = begin + 1; i != end + 1; i++) {
for (int j = i; j != begin; j--) {
if (arr[j - 1] > arr[j]) {
swap(arr, j - 1, j);
} else {
break;
}
}
}
}


public static void swap(int[] arr, int index1, int index2) {
int tmp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = tmp;
}


public static void printArray(int[] arr) {
for (int i = 0; i != arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}


public static void main(String[] args) {
int[] arr = { 6, 9, 1, 3, 1, 2, 2, 5, 6, 1, 3, 5, 9, 7, 2, 5, 6, 1, 9 };
// sorted : { 1, 1, 1, 1, 2, 2, 2, 3, 3, 5, 5, 5, 6, 6, 6, 7, 9, 9, 9 }
printArray(getMinKNumsByHeap(arr, 10));
printArray(getMinKNumsByBFPRT(arr, 10));


}


}







C++程序:

//求第k小的数
#include<iostream>
#include<stdlib.h>
using namespace std;


int partition(int* a,int low,int high)
{
int temp=a[low];
int curr=low;
int dex=low-1;
while(low<high)
{
while(low<high&&a[high]>temp)
high--;
if(low<high)a[low]=a[high];
while(low<high&&a[low]<=temp)
low++;
if(low<high)a[high]=a[low];
    }
a[low]=temp;
return low;
}
void found(int* a,int low,int high,int k)
{
int temp=partition(a,low,high);
if(temp==(k-1))
cout<<"第k小数"<<a[temp]<<endl;
else if(temp>(k-1))
found(a,low,temp-1,k);
else
found(a,temp+1,high,k);
}


int main()
{
int a[10]={15,25,9,48,36,100,58,99,126,5};
int n;
while(cin>>n)
{
found(a,0,9,n);
}


system("pause");
return 0;
}



原创粉丝点击