LeetCode——Maximum Gap 最大间隙问题

Q:给定n个实数x1,x2,...,xn,求这n个实数在实轴上相邻2个数之间的最大差值,要求设计线性的时间算法
A:
最简单的是直接用sort,然后一个一个计算gap,再找到最大,但sort函数本身是快速排序,默认时间为O(nlogn)。
有三种线性的时间复杂度的排序算法,桶排序、基数排序、计数排序,三种排序的应用场景有限。

使用桶排序:

(1)介绍桶排序
一句话总结:划分多个范围相同的区间,每个自区间自排序,最后合并。
桶排序是计数排序的扩展版本,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素映射到各个对应的桶中,对每个桶中的元素进行排序,最后将非空桶中的元素逐个放入原序列中。
桶排序需要尽量保证元素分散均匀,否则当所有数据集中在同一个桶中时,桶排序失效。

(2)桶排序解决最大间隙问题

  1. 找到n个数据中最大和最小数据maxx和minx;
  2. 用n-2个点等分区间[minx,maxx],即将[minx,maxx]等分为n-1个区间(前闭后开区间),将这些区间看做桶,编号为1,2,...,n-2,n-1,且桶i的上界和桶i+1的下届相同,即每个桶的大小相同;
        每个桶的大小为: dblAvrGap=(maxx-minx)/(n-1)
        实际上,这些桶的边界就构成了一个等差数列(首项为minx,公差d=dblAvrGap),且人为将minx放入第1个桶,将maxx放入第n-1个桶。
                                    
        编程实现中,用以下数据结果存放有关桶的数据:
                int *count=new int[n];  //实际分到每个桶的数据个数
                double *low=new double[n]; //实际分到每个桶的最小数据
                double *high=new double[n]; //实际分到每个桶的最大数据
  3. 将n个数放入n-1个桶中:
         3.1 按如下规则将x[i]分配到某个桶(编号index)中:  index=int((x[i]-minx)/dblAvrGap)+1;
                    若x[i]=minx,则被分到第1个桶中(minx即为桶1的下界);
                    若x[i]=桶j的下界(也是桶j-1的上界),则被分到桶j中(j>=1);
                    若x[i]=maxx,则被分到桶n中(max为桶n的下界桶n-1的上界),但没有桶n,解决办法:
                            可人为将其移入桶n-1中或者再加一个桶,这并不影响求其最大间隙;
                                               
          3.2 调整分到该桶的最大最小数据;
  4. 求最大间隙:
          除最大最小数据maxx和minx以外的n-2个数据被放入n-1个桶中,由抽屉原理可知至少有一个桶是空的;
          又因每个桶的大小相同,所以最大间隙不会在同一桶中出现;
          一定是某个桶的上界(dblHigh)和其后某个桶的下界(dblLow)之间隙,且该两桶之间的桶(即编号在该两桶编号之间的桶)一定是空桶;
          即最大间隙在桶i的上界和桶j的下界之间产生(j>=i+1);

代码:

    public static int maximumGap(ArrayList<Integer> num) {
        if (num.size() < 2)
            return 0;
        int maxNum = num.get(0);
        int minNum = num.get(0);
        for (int i = 1; i < num.size(); i++) {
            maxNum = Math.max(maxNum, num.get(i));
            minNum = Math.min(minNum, num.get(i));
        }
        //桶的长度
        int len = (maxNum - minNum) / num.size() + 1;
        //桶的个数
        int size = (maxNum - minNum) / len + 1;
        ArrayList<ArrayList<Integer>> buckets = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            buckets.add(new ArrayList<>());
        }
        for (Integer integer : num) {
            int j = (integer - minNum) / len;
            if (buckets.get(j).isEmpty()) {
                buckets.get(j).add(integer);
            } else {
                //插入排序调节内部顺序。实际上该题只需要找到该桶最小和最大即可,不需要这么麻烦。用插入排序是熟悉一下这种方法
                boolean flag = false;
                for (int k = 0; k < buckets.get(j).size(); k++) {
                    if (buckets.get(j).get(k) > integer) {
                        buckets.get(j).add(k, integer);
                        flag = true;
                        break;
                    }
                }
                if (!flag)
                    buckets.get(j).add(integer);
            }
        }
        int gap = 0;
        int prev = 0;
        for (int i = 1; i < buckets.size(); i++) {
            if (buckets.get(i).isEmpty())
                continue;
            gap = Math.max(gap, buckets.get(i).get(0) - buckets.get(prev).get(1));
            prev = i;
        }
        return gap;
    }

使用基数排序

    public static int maximumGap1(ArrayList<Integer> num) {
        if (num.size() < 2)
            return 0;
        int maxNum = num.get(0);
        for (Integer n : num) {
            maxNum = Math.max(maxNum, n);
        }
        int bit = 0;
        while (true) {
            if (maxNum >= Math.pow(10, bit)) {
                bit++;
            } else
                break;
        }
        for (int i = 0; i < bit; i++) {
            ArrayList<ArrayList<Integer>> bucket = new ArrayList<>();
            for (int j = 0; j < 10; j++) {
                bucket.add(new ArrayList<>());
            }
            for (Integer n : num) {
                int a  = (int) (n / Math.pow(10, i));
                int bitNum = a % 10;
                bucket.get(bitNum).add(n);
            }
            num.clear();
            for (ArrayList<Integer> nums : bucket) {
                num.addAll(nums);
            }
        }
        int maxGap = 0;
        for (int i = 1; i < num.size(); i++) {
            maxGap = Math.max(maxGap, num.get(i) - num.get(i - 1));
        }
        return maxGap;
    }

引用:https://www.jianshu.com/p/94f08afe4ecb
https://blog.csdn.net/jiyanfeng1/article/details/39312011
https://blog.csdn.net/qq_27124771/article/details/87651495

原文地址:https://www.cnblogs.com/xym4869/p/12545631.html