数据结构与算法之BFPRT算法

傷城~ 2021-12-17 01:25 354阅读 0赞

数据结构与算法之BFPRT算法


目录

  1. BFPRT算法介绍
  2. BFPRT算法代码实现

1. BFPRT算法介绍

引用自博客:BFPRT算法O(n)解决第k小的数:https://www.jianshu.com/p/a43b0e1712d1

在一大堆数中求其前k大或前k小的问题,简称TOP-K问题。而目前解决TOP-K问题最有效的算法即是BFPRT算法,其又称为中位数的中位数算法,该算法由Blum、Floyd、Pratt、Rivest、Tarjan提出,最坏时间复杂度为O(n)。

我们通常会简单地进行一个快速排序后,得到第k个位置上的数字即可。
我们都知道的是快速排序是个不稳定的排序,它的排序过程简单的理解主要是两个概念Partion,pivot(基准数)

一趟快速排序的过程如下:

  1. 先从序列中选取一个数作为基准数
  2. 将比这个数大的数全部放到它的右边,把小于或者等于它的数全部放到它的左边

一趟快速排序也叫做Partion,即将序列划分为两部分,一部分比基准数小,另一部分比基准数大,然后再进行分治过程,因为每一次Partion不一定都能保证划分得很均匀,所以最坏情况下的时间复杂度不能保证总是O(nlogn)的。


BFPRT算法
  1. 在BFPTR算法中,仅仅是改变了快速排序Partion中的pivot值的选取,在快速排序中,我们始终选择第一个元素或者最后一个元素作为pivot,而在BFPTR算法中,每次选择五分中位数的中位数作为pivot,这样做的目的就是使得划分比较合理,从而避免了最坏情况的发生。
  2. 算法步骤如下
    (1)将输入数组的n个元素划分为n/5组,每组5个元素,且至多只有一个组由剩下的n%5个元素组成。
    (2)寻找n/5个组中每一个组的中位数,首先对每组的元素进行插入排序,然后从排序过的序列中选出中位数。
    (3)对于(2)中找出的n/5个中位数,递归进行步骤(1)和(2),直到只剩下一个数即为这n/5个元素的中位数,找到中位数后并找到对应的下标p。
    (4)进行Partion划分过程,Partion划分中的pivot元素下标为p。
    (5)进行高低区判断即可。
  3. 本算法的最坏时间复杂度为O(n),值得注意的是通过BFPTR算法将数组按第K小(大)的元素划分为两部分,而
    这高低两部分不一定是有序的,通常我们也不需要求出顺序,而只需要求出前K大的或者前K小的。

2. BFPRT算法代码实现

附上堆排序下找k小的值和BFPRT算法的代码实现

  1. public class Code_06_BFPRT {
  2. // O(N*logK)
  3. public static int[] getMinKNumsByHeap(int[] arr, int k) {
  4. if (k < 1 || k > arr.length) {
  5. return arr;
  6. }
  7. int[] kHeap = new int[k];
  8. for (int i = 0; i != k; i++) {
  9. heapInsert(kHeap, arr[i], i);
  10. }
  11. for (int i = k; i != arr.length; i++) {
  12. if (arr[i] < kHeap[0]) {
  13. kHeap[0] = arr[i];
  14. heapify(kHeap, 0, k);
  15. }
  16. }
  17. return kHeap;
  18. }
  19. public static void heapInsert(int[] arr, int value, int index) {
  20. arr[index] = value;
  21. while (index != 0) {
  22. int parent = (index - 1) / 2;
  23. if (arr[parent] < arr[index]) {
  24. swap(arr, parent, index);
  25. index = parent;
  26. } else {
  27. break;
  28. }
  29. }
  30. }
  31. public static void heapify(int[] arr, int index, int heapSize) {
  32. int left = index * 2 + 1;
  33. int right = index * 2 + 2;
  34. int largest = index;
  35. while (left < heapSize) {
  36. if (arr[left] > arr[index]) {
  37. largest = left;
  38. }
  39. if (right < heapSize && arr[right] > arr[largest]) {
  40. largest = right;
  41. }
  42. if (largest != index) {
  43. swap(arr, largest, index);
  44. } else {
  45. break;
  46. }
  47. index = largest;
  48. left = index * 2 + 1;
  49. right = index * 2 + 2;
  50. }
  51. }
  52. // O(N)
  53. public static int[] getMinKNumsByBFPRT(int[] arr, int k) {
  54. if (k < 1 || k > arr.length) {
  55. return arr;
  56. }
  57. int minKth = getMinKthByBFPRT(arr, k);
  58. int[] res = new int[k];
  59. int index = 0;
  60. for (int i = 0; i != arr.length; i++) {
  61. if (arr[i] < minKth) {
  62. res[index++] = arr[i];
  63. }
  64. }
  65. for (; index != res.length; index++) {
  66. res[index] = minKth;
  67. }
  68. return res;
  69. }
  70. public static int getMinKthByBFPRT(int[] arr, int K) {
  71. int[] copyArr = copyArray(arr);
  72. return select(copyArr, 0, copyArr.length - 1, K - 1);
  73. }
  74. public static int[] copyArray(int[] arr) {
  75. int[] res = new int[arr.length];
  76. for (int i = 0; i != res.length; i++) {
  77. res[i] = arr[i];
  78. }
  79. return res;
  80. }
  81. public static int select(int[] arr, int begin, int end, int i) {
  82. if (begin == end) {
  83. return arr[begin];
  84. }
  85. int pivot = medianOfMedians(arr, begin, end);
  86. int[] pivotRange = partition(arr, begin, end, pivot);
  87. if (i >= pivotRange[0] && i <= pivotRange[1]) {
  88. return arr[i];
  89. } else if (i < pivotRange[0]) {
  90. return select(arr, begin, pivotRange[0] - 1, i);
  91. } else {
  92. return select(arr, pivotRange[1] + 1, end, i);
  93. }
  94. }
  95. public static int medianOfMedians(int[] arr, int begin, int end) {
  96. int num = end - begin + 1;
  97. int offset = num % 5 == 0 ? 0 : 1;
  98. int[] mArr = new int[num / 5 + offset];
  99. for (int i = 0; i < mArr.length; i++) {
  100. int beginI = begin + i * 5;
  101. int endI = beginI + 4;
  102. mArr[i] = getMedian(arr, beginI, Math.min(end, endI));
  103. }
  104. return select(mArr, 0, mArr.length - 1, mArr.length / 2);
  105. }
  106. public static int[] partition(int[] arr, int begin, int end, int pivotValue) {
  107. int small = begin - 1;
  108. int cur = begin;
  109. int big = end + 1;
  110. while (cur != big) {
  111. if (arr[cur] < pivotValue) {
  112. swap(arr, ++small, cur++);
  113. } else if (arr[cur] > pivotValue) {
  114. swap(arr, cur, --big);
  115. } else {
  116. cur++;
  117. }
  118. }
  119. int[] range = new int[2];
  120. range[0] = small + 1;
  121. range[1] = big - 1;
  122. return range;
  123. }
  124. public static int getMedian(int[] arr, int begin, int end) {
  125. insertionSort(arr, begin, end);
  126. int sum = end + begin;
  127. int mid = (sum / 2) + (sum % 2);
  128. return arr[mid];
  129. }
  130. public static void insertionSort(int[] arr, int begin, int end) {
  131. for (int i = begin + 1; i != end + 1; i++) {
  132. for (int j = i; j != begin; j--) {
  133. if (arr[j - 1] > arr[j]) {
  134. swap(arr, j - 1, j);
  135. } else {
  136. break;
  137. }
  138. }
  139. }
  140. }
  141. public static void swap(int[] arr, int index1, int index2) {
  142. int tmp = arr[index1];
  143. arr[index1] = arr[index2];
  144. arr[index2] = tmp;
  145. }
  146. public static void printArray(int[] arr) {
  147. for (int i = 0; i != arr.length; i++) {
  148. System.out.print(arr[i] + " ");
  149. }
  150. System.out.println();
  151. }
  152. public static void main(String[] args) {
  153. int[] arr = {
  154. 6, 9, 1, 3, 1, 2, 2, 5, 6, 1, 3, 5, 9, 7, 2, 5, 6, 1, 9 };
  155. // sorted : { 1, 1, 1, 1, 2, 2, 2, 3, 3, 5, 5, 5, 6, 6, 6, 7, 9, 9, 9 }
  156. printArray(getMinKNumsByHeap(arr, 10));
  157. printArray(getMinKNumsByBFPRT(arr, 10));
  158. }
  159. }

发表评论

表情:
评论列表 (有 0 条评论,354人围观)

还没有评论,来说两句吧...

相关阅读

    相关 数据结构算法算法分析

    算法的五个重要特征:有穷性,确定性,可行性,输入,输出。 输入,是指算法具有零个或多个输入。 输出,是指算法至少有一个或多个输出。 有穷性,是指算法在执行有限的步骤之后,

    相关 BFPRT算法

    一、先来看一个问题 在一个乱序的数组中,寻找第k个小的值? 很多人第一种解法,用大顶堆,然后poll第k个就是答案了,但是时间复杂度是O(nlogn),有没有O(n)的