算法分析-TOP-K问题-BFPRT算法

关于Top-K问题,我们最容易想到的常规算法是是先排序,再返回第K个元素,快速排序的平均复杂度为O(nlogn),最坏时间复杂度为O(n2),不能始终保证较好的复杂度。这里我们只需要Top-K个元素或者是第k个元素,对其他元素并不关心,对n个数全部进行排序显然是不合理的,那么有没有一种更快的算法呢?

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


具体步骤:

1、将n个数分成n/5个组,每组5个元素,剩余的元素舍弃,对这五个元素进行排序(数量较少一般采用插入排序)

2、对每组的5个数求中位数,然后求中位数的中位数,实现起来通常将每组的中位数与前面的n/5个数交换,这样中位数又是连续的了,可以递归继续求中位数,直到最后求得一个数,记住这个数及其位置

3、运用快速排序的思想以这个中位数作为主元(pivot),进行快速交换排序,这样此数组刚好被这个主元分成两部分,一部分大于这个主元,一部分小于这个主元,记住主元的位置pos。

4、如果这个pos == k,则直接返回pos对应的值

     如果pos<k,则 返回到第1步,递归调用,只用算数组left~pos-1的部分

     如果pos>k,则 返回到第1步,递归调用,只用算数组pos+1~right的部分

import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
 * 
 * 
7 2
2 3 4 7 5 9 1
 *
 */
public class TopK_BFPRT {


  static int N;
  static int K;
  static int[] array;
  public static void main(String[] args) throws Exception {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String line = br.readLine();
    String[] sa = line.split(" ");
    N = Integer.valueOf(sa[0]);
    K = Integer.valueOf(sa[1]);
    line = br.readLine();
    sa = line.split(" ");
    array = new int[N];
    for(int i=0; i<N; i++)
      array[i] = Integer.valueOf(sa[i]);
    System.out.println(bfprt(0, N-1, K));
  }
 /**
 * bfprt中位数的中位数算法
 * @param left
 * @param right
 * @param k
 * @return
 */
  private static int bfprt(int left, int right, int k){
    int midPos = getMidPos(left, right);
    int value = partition(left, right, midPos)+1;
    if(value == k){
      return array[k-1];
    }else if(value > k){
      return bfprt(left, value-2, k);
    }else{
      return bfprt(value, right, k);
    }
  }
  /**
   * 快速交换
   * @param left
   * @param right
   * @param midpos
   * @return
   */
  private static int partition(int left, int right, int midpos){
    int pivot = array[midpos];
    swap(midpos, left);
    int l = left;
    int r = right;
    while(l<r){
      if(array[l] < array[r]){
        r--;
      }else{
        swap(l, r);
        if(pivot == array[l])
          r--;
        else
          l++;
      }
    }
    return l;
  }
  /***
   * 求中位数的中位数
   * @param left
   * @param right
   * @return
   */
  private static int getMidPos(int left, int right){
    if(right - left <= 5)
      return insertSort(left, right);
    int bleft = left;
    for(int i=left; i+4<=right; i+=5){
      int midv = insertSort(i, i+4);
      swap(bleft++, midv);
    }
    return getMidPos(left, bleft);
  }
  /**
   * 插入排序
   * @param left
   * @param right
   * @return
   */
  private static int insertSort(int left, int right){
    for(int i = left + 1; i<=right; i++){
      for(int j = i; j > left; j--){
        if(array[j] < array[j-1]){
          swap(j, j-1);
        }
      }
    }
    return (left+right)/2;
  }
  /**
   * 数据交换
   * @param pos1
   * @param pos2
   */
  private static void swap(int pos1, int pos2){
    int temp = array[pos1];
    array[pos1] = array[pos2];
    array[pos2] = temp;
  }
}

时间复杂度分析:

求中位数的中位数,首先用到了插入排序,插入排序5个数的时间复杂度为10,分成n/5组,约为O(2n+2n/5+2n/25+.....+1)约等于O(2.5n)

交换快速排序:O(n)

一次递归时间为O(2.5n)+O(n) = O(3.5n)然后二分求值O(3.5n)+O(3.5n/2)+O(3.5n/4)+......+1 约等于O(5n)由于5是常数所有时间复杂度为O(n).



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BFPRT算法是一种解决Top-K问题算法,其中K为要找的第K大的元素。该算法的时间复杂度为O(n)。 BFPRT算法的基本思想是通过划分算法来找到中位数,然后根据中位数将数组分成两部分,只需要在一部分中查找Top-K,另一部分不用查找。 以下是BFPRT算法的c语言实现: ```c #include <stdio.h> #include <stdlib.h> #define SWAP(a, b) {int temp = a; a = b; b = temp;} // 求中位数 int median(int arr[], int left, int right) { int center = (left + right) / 2; if (arr[left] > arr[center]) SWAP(arr[left], arr[center]) if (arr[left] > arr[right]) SWAP(arr[left], arr[right]) if (arr[center] > arr[right]) SWAP(arr[center], arr[right]) SWAP(arr[center], arr[right - 1]) return arr[right - 1]; } // 划分函数 int partition(int arr[], int left, int right, int pivot) { int i, j; for (i = left, j = right - 2;;) { while (arr[++i] < pivot); while (arr[--j] > pivot); if (i < j) SWAP(arr[i], arr[j]) else break; } SWAP(arr[i], arr[right - 1]) return i; } // BFPRT算法 int bfprt(int arr[], int left, int right, int k) { if (left + 1 == right) return arr[left]; int pivot = median(arr, left, right); int mid = partition(arr, left, right, pivot); int num = mid - left + 1; if (k == num) return arr[mid]; else if (k < num) return bfprt(arr, left, mid, k); else return bfprt(arr, mid + 1, right, k - num); } int main() { int arr[] = {3, 4, 2, 1, 5, 6, 8, 7, 9}; int len = sizeof(arr) / sizeof(int); int k = 5; int kth = bfprt(arr, 0, len, k); printf("The %dth number is: %d\n", k, kth); return 0; } ``` 该算法的核心是BFPRT划分算法,该算法是一个递归算法,每次递归的时间复杂度为O(n),因此总的时间复杂度为O(nlogn)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值