荷兰国旗问题&快排&BFPRT算法

荷兰国旗问题

给定一个数组arr和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。要求额外空间复杂度为O(1),时间复杂度为O(N)

解决思路

初始化less=-1,more=len(arr),当前位置为cur=0。

  • 如果arr[cur]<num,交换arr[cur]和arr[++less]的数,然后cur++
  • 如果arr[cur]>num,交换arr[cur]和arr[–more]的数,然后cur不变
  • 如果当前位置上的数等于num,less和more均不变,cur++
  • 当cur==more时,停止比较,返回

代码

public class NetherLandsFlag{
    public static int[] partition(int[] arr, int L, int R, int num) {
        int less = L - 1;//小于区域的边界
        int more = R + 1;//大于区域的边界
        int cur = 0;
        while (cur < more) {
            if (arr[cur] < num) {
                swap(arr, ++less, cur++);
            } else if (arr[cur] > num) {
                swap(arr, --more, cur);
            } else {
                cur++;
            }
        }
        return arr;
    }
    
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    
    public static void main(String[] args){
        int[] arr = new int[]{7, 2, 9, 3, 5, 8, 8, 5, 1, 6, 6, 4};
        int[] res = partition(arr, 0, arr.length - 1, 5);
        System.out.println(Arrays.toString(res));
    }
}

运行截图

在这里插入图片描述

稳定算法的定义

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这个排序算法是稳定的;否则称为不稳定的。


BFPRT算法

背景介绍

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

一般第一反应解决TOP-K问题就是对所有数据进行一次排序,然后取其前k即可,但是有两个问题:

  1. 排序的平均复杂度为O(n*logn),最坏时间复杂度为O(n^2),不能保证较好的复杂度
  2. 我们只需要前k大的数,而对其余不需要的数也进行了排序,浪费了大量排序时间

除了这种方法之外,堆排序也是一个比较好的选择, 可以维护一个大小为k的堆,时间复杂度为O(n*logk)。

接下来讲解的是BFPRT算法,解决TOP-K问题的最优算法

算法套路

  1. 对整个数组进行分组,每组5个数,不满5个的凑成最后一组
  2. 对每个组进行组内排序,组内5个数排序的时间复杂度为O(1),所以总共有n/5个组,时间复杂度为O(N)
  3. 拿出排序后的每个组的中位数,组成一个新的n/5长度的数组
  4. 递归调用BFPRT算法,求出最后的中位数num
  5. 拿到BFPRT的返回的num,利用荷兰国旗算法把小于的放在数组的左边,等于的放在数组的中间,大于放在数组的右边。

伪代码

public class BFPRT{
	public int bfprt(int[] arr,int k){
        //1、对arr数组进行分组,每5个为一组
        //2、进行组内排序,时间复杂度为O(N),取出每个组的中位数
        //3、每个组的中位数组成一个n/5的数组new_arr
        //4、递归bfprt(new_arr,new_arr.length/2)
        //5、得到一个中位数num
    }
    //利用这个中位数进行荷兰国旗算法,小于在左边,等于在中间,大于在右边
    //如果num==k,则该中位数就是第k大的数
    //如果num<k,则递归求num之后的数组
    //如果num>k,则递归求num之前的数组
}

时间复杂度证明

在这里插入图片描述

总结

以上就是从一个荷兰国旗引起的快排和BFPRT算法,理解之后也挺有意思的,然后就是BFPRT算法的时间复杂度是严格的O(N),但是对证明不感兴趣,证明过程的来源是csdn博客一位大神的证明过程,我拿过来给有兴趣的人看一下他的时间复杂度怎么达到O(N)的。

猜你喜欢

转载自blog.csdn.net/weixin_37686415/article/details/106052415
今日推荐