2020_11_26 每日一题 164. 最大间距

给定一个无序的数组,找出数组在排序之后,相邻元素之间最大的差值。

如果数组元素个数小于 2,则返回 0。

示例 1:

输入: [3,6,9,1]

输出: 3

解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。

示例 2:

输入: [10]

输出: 0

解释: 数组元素个数小于 2,因此返回 0。

说明:

你可以假设数组中所有元素都是非负整数,且数值在 32 位有符号整数范围内。

请尝试在线性时间复杂度和空间复杂度的条件下解决此问题。

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/maximum-gap

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目标的困难,但是如果不看那句线性时空还是很轻松的.:

class Solution {

    public int maximumGap(int[] nums) {
        //线性时空想不出来啊...
        Arrays.sort(nums);
        int max = 0;
        for(int i = 1; i < nums.length; ++i) {
            if(nums[i] - nums[i - 1] > max) {
                max = nums[i] - nums[i - 1];
            }
        }
        return max;
    }
}

java的排序似乎用的是快排,所以时间是nlgn

看题解看看有什么线性时空的方法,先是基排序,这个之前在算法导论里看过,总之是先按个位排序,然后按十位排序依次类推。每一位排序的时候要求稳定,用桶排序就好。这样时间复杂度是n * d d是最高位数,空间是n + r r是基数的大小
容易得知基数越大最高位数越小…所以这个时空是可以调配的
实现:

class Solution {

    static int base = 1000;//基数

    int getK(int num, int k) {
        //选中num的第k位 从0开始算
        for(int i = 0; i < k; ++i) {
            num /= base;
        }
        return num % base;
    }

    int findMaxK(int num) {
        //1返回1 10返回2 类似这样
        int i = 0;
        while(num != 0) {
            num /= base;
            ++i;
        }
        return i;
    }

    int[] sortBy(int[] nums, int k) {
        //以最低k位排序
        List[] buckets = new List[base];
        for(int i = 0; i < base; ++i) {
            buckets[i] = new ArrayList<Integer>();
        }
        for(int i : nums) {
            int ii = getK(i, k);
            buckets[ii].add(i);
        }
        int i = 0, lIndex = 0, inList = 0;
        while(i < nums.length) {
            while(buckets[lIndex].size() == inList) {
                inList = 0;
                ++lIndex;
            }
            nums[i++] = (Integer)buckets[lIndex].get(inList++);
        }
        return nums;
    }

    int[] radixSort(int[] nums) {
        int maxK = 0;
        for(int i : nums) {
            maxK = Math.max(maxK, findMaxK(i));
        }
        //System.out.println(maxK);
        for(int i = 0; i < maxK; ++i) {
            nums = sortBy(nums, i);
        }
        return nums;
    }

    public int maximumGap(int[] nums) {
        //基排序
        radixSort(nums);
        int max = 0;
        for(int i = 1; i < nums.length; ++i) {
            if(nums[i] - nums[i - 1] > max) {
                max = nums[i] - nums[i - 1];
            }
        }
        return max;
    }
}

实际算起来时间要比用java自带的排序慢了好多…还是数组元素不够多啊

题解还给了一种分桶的,思路是:
把数放到若干个桶里。如果我能保证每个桶内不存在最大间距,那么我只需要维护每个桶里的最大值和最小值,算桶间最大间距就好。
至于桶的大小,如果我能知道最大间距的最小值,把桶设的比这个值小,桶内就一定不会出现最大间距的两个数。
容易知道平分的时候最大间距最小(不平分的时候总是会有一个间距更大),所以桶的大小设成比(max - min) / (n - 1)小 桶内一定不会出现最大间距 算出上面那个数,向下取整作为桶的大小就好
实现:

class Solution {

    int getMin(int[] nums) {
        int min = Integer.MAX_VALUE;
        for(int i : nums) {
            min = Math.min(min, i);
        }
        return min;
    }

    int getMax(int[] nums) {
        int max = Integer.MIN_VALUE;
        for(int i : nums) {
            max = Math.max(max, i);
        }
        return max;
    }

    public int maximumGap(int[] nums) {
        //分桶
        if(nums.length < 2) {
            return 0;
        }
        int min = getMin(nums), max = getMax(nums);
        int bucketSize = Math.max((max - min) / (nums.length - 1), 1);
        if(min == max) {
            return 0;
        }
        Integer[] mins = new Integer[(max - min) / bucketSize + 2];
        Integer[] maxs = new Integer[(max - min) / bucketSize + 2];
        for(int i : nums) {
            //先算出应该在哪个桶
            int bucketIndex = (i - min) / bucketSize;
            if(mins[bucketIndex] == null || mins[bucketIndex] > i) {
                mins[bucketIndex] = i;
            }
            if(maxs[bucketIndex] == null || maxs[bucketIndex] < i) {
                maxs[bucketIndex] = i;
            }
        }
        int l = 0, r = 0;
        while(mins[l] == null) {
            ++l;
        }
        r = l;
        int ret = 0;
        while(l < mins.length) {
            r = l + 1;
            while(r < mins.length && mins[r] == null) {
                ++r;
            }
            if(r < mins.length) {
                ret = Math.max(ret, mins[r] - maxs[l]);
            }
            l = r;
        }
        return ret;
    }
}

有一些细节需要注意
1.bucketSize不能为0 bucketSize为1的时候其实退化成了桶排序
2.min == max的情形要特殊处理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值