bfprt算法、蓄水池算法

113 阅读1分钟

在无序数组中求第K小的数

1)改写快排的方法

// 改写快排,时间复杂度O(N)
// k >= 1
public static int minKth2(int[] array, int k) {
   int[] arr = copyArray(array);
   return process2(arr, 0, arr.length - 1, k - 1);
}

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

// arr 第k小的数
// process2(arr, 0, N-1, k-1)
// arr[L..R]  范围上,如果排序的话(不是真的去排序),找位于index的数
// index [L..R]
public static int process2(int[] arr, int L, int R, int index) {
   if (L == R) { // L = =R ==INDEX
      return arr[L];
   }
   // 不止一个数  L +  [0, R -L]
   int pivot = arr[L + (int) (Math.random() * (R - L + 1))];
   int[] range = partition(arr, L, R, pivot);
   if (index >= range[0] && index <= range[1]) {
      return arr[index];
   } else if (index < range[0]) {
      return process2(arr, L, range[0] - 1, index);
   } else {
      return process2(arr, range[1] + 1, R, index);
   }
}

public static int[] partition(int[] arr, int L, int R, int pivot) {
   int less = L - 1;
   int more = R + 1;
   int cur = L;
   while (cur < more) {
      if (arr[cur] < pivot) {
         swap(arr, ++less, cur++);
      } else if (arr[cur] > pivot) {
         swap(arr, cur, --more);
      } else {
         cur++;
      }
   }
   return new int[] { less + 1, more - 1 };
}

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

2)bfprt算法

// 利用bfprt算法,时间复杂度O(N)
public static int minKth3(int[] array, int k) {
   int[] arr = copyArray(array);
   return bfprt(arr, 0, arr.length - 1, k - 1);
}

// arr[L..R]  如果排序的话,位于index位置的数,是什么,返回
public static int bfprt(int[] arr, int L, int R, int index) {
   if (L == R) {
      return arr[L];
   }
   // L...R  每五个数一组
   // 每一个小组内部排好序
   // 小组的中位数组成新数组
   // 这个新数组的中位数返回
   int pivot = medianOfMedians(arr, L, R);
   int[] range = partition(arr, L, R, pivot);
   if (index >= range[0] && index <= range[1]) {
      return arr[index];
   } else if (index < range[0]) {
      return bfprt(arr, L, range[0] - 1, index);
   } else {
      return bfprt(arr, range[1] + 1, R, index);
   }
}

// arr[L...R]  五个数一组
// 每个小组内部排序
// 每个小组中位数领出来,组成marr
// marr中的中位数,返回
public static int medianOfMedians(int[] arr, int L, int R) {
   int size = R - L + 1;
   int offset = size % 5 == 0 ? 0 : 1;
   int[] mArr = new int[size / 5 + offset];
   for (int team = 0; team < mArr.length; team++) {
      int teamFirst = L + team * 5;
      // L ... L + 4
      // L +5 ... L +9
      // L +10....L+14
      mArr[team] = getMedian(arr, teamFirst, Math.min(R, teamFirst + 4));
   }
   // marr中,找到中位数
   // marr(0, marr.len - 1,  mArr.length / 2 )
   return bfprt(mArr, 0, mArr.length - 1, mArr.length / 2);
}

public static int getMedian(int[] arr, int L, int R) {
   insertionSort(arr, L, R);
   return arr[(L + R) / 2];
}

topK问题

给定一个无序数组arr中,长度为N,给定一个正数k,返回top k个最大的数

不同时间复杂度三个方法:

1)O(N*logN) 排序找第k个

2)O(N + K*logN) 用堆弹出k个

3)O(n + k*logk) 求出第N-k个小的数,也就是第k个大的数,把arr中比这个数大的数都收集起来排个序即可

蓄水池算法

解决的问题:

假设有一个源源吐出不同球的机器,

只有装下10个球的袋子,每一个吐出的球,要么放入袋子,要么永远扔掉

如何做到机器吐出每一个球之后,所有吐出的球都等概率被放进袋子里

public static void main(String[] args) {
   System.out.println("hello");
   int test = 10000;
   int ballNum = 17;
   int[] count = new int[ballNum + 1];
   for (int i = 0; i < test; i++) {
      int[] bag = new int[10];
      int bagi = 0;
      for (int num = 1; num <= ballNum; num++) {
         if (num <= 10) {
            bag[bagi++] = num;
         } else { // num > 10
            if (random(num) <= 10) { // 一定要把num球入袋子
               bagi = (int) (Math.random() * 10);
               bag[bagi] = num;
            }
         }
      }
      for (int num : bag) {
         count[num]++;
      }
   }
   for (int i = 0; i <= ballNum; i++) {
      System.out.println(count[i]);
   }
}