BFPRT算法之js实现

4.BFPRT算法

解决问题:求一个数组中第k小或第k大的数。复杂度O(n)。
  算法流程:
a) 将数组每5个划成一组;
b) 每个组内排序,分别取每一组中的中位数组成一个新的数组;
c) 将新的数组进行排序,取其中的中位数a作为参考值;
d) 取a对原数组进行比较,把小于a的数放在左边,等于a的放中间,大于a的放右边,看a是否是第k小的数,若不是再从左边或右边重复同一过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
function getMinKthByBFPRT(arr, k) {
var copyArr = arr.slice();
return select(copyArr, 0, copyArr.length - 1, k - 1)
}
/**
* 求出arr数组中从begin到end之间的子数组中第i小上的数
* @param arr
* @param begin
* @param end
* @param i
* @returns {*}
*/
function select(arr, begin, end, i) {
if (begin === end) {
return arr[begin];
}
var pivot = medianOfMedians(arr, begin, end);
var 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);
}
}
/**
* 给数组分组,并求出每组数组中位数,返回中位数组成数组中的中位数
* @param arr
* @param begin 原始数组中子数组开始位置
* @param end 原始数组中子数组结束位置
* @returns {*} 划分所用的
*/
function medianOfMedians(arr, begin, end) {
var num = end - begin + 1;
var mArr = new Array(Math.ceil(num / 5)); //中位数数组
for (var i = 0; i < mArr.length; i++) {
var beginI = begin + i * 5;
var endI = beginI + 4;
mArr[i] = getMedian(arr, beginI, Math.min(end, endI));
}
return select(mArr, 0, mArr.length - 1, parseInt(mArr.length / 2));//求中位数数组的上中位数
}


function partition(arr, begin, end, pivotValue) {
var small = begin - 1;
var cur = begin;
var 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++;
}
var range = [];
range[0] = small + 1;
range[1] = big - 1;
return range;
}
}
/**
* 得到五位数组的中位数
* @param arr
* @param begin
* @param end
* @returns {*}
*/
function getMedian(arr, begin, end) {
insertSort(arr, begin, end);
var sum = end + begin;
var mid = Math.ceil(sum / 2);
return arr[mid];
}
/**
* 插入排序
* @param arr
* @param begin
* @param end
*/
function insertSort(arr, begin, end) {
for (var i = begin - 1; i < end + 1; i++) {
for (var j = i; j !== begin; j--) {//依次将第j个插入到前面排序好的j-1数组中
if (arr[j - 1] > arr[j]) {
swap(arr, j - 1, j);
} else {
break
}
}
}
}
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
console.log(getMinKthByBFPRT([5, 6, 3, 2, 6, 7, 8], 3)); //5