非基于比较的排序算法例题

题目:

给定一个数组,求如果排序之后,相邻两数的最大差值,要求时间复杂度O(N),且要求不能用非基于比较的排序。

例:[3,1,6,2,7]排序后得到[1,2,3,6,7],并且相邻两数的最大差值是3(元素3和元素6的差值)。

解决思路:

       利用到桶的思想,数组有n个数,定义n+1个桶,遍历数组找到最小值和最大值,放在桶的前后两端。然后min-max范围之间的数,分成n+1份,元素属于哪个范围,就放到哪个桶内。例子:设数组长度为9,最小值是0,最大值是99!则需要准备10个桶,0放在0号桶,99放在9号桶。则0-99范围的数分成10份,即0-9、10-19、......、90-99这10个区间,也就是10个桶。所以元素属于哪个范围,就放到哪个桶内。

       因为首尾两个桶必不为空,则中间至少有一个空桶。则排序后的相邻两个数既可能来自同一个桶,也可能来自不同桶。则依据该空桶,找到该空桶左边最近的非空桶和右边最近的非空桶,此时左边非空桶中的最大值和右边非空桶中的最小值,在排序后绝对是相邻的,而此时这两个极值的差值又绝对比桶的范围要大,也就是说肯定比一个桶内的两个元素差值大。所以说,最大差值只能来自不同的桶内元素。因此只需要统计每个桶内的最小值和最大值即可,不用记录进入桶内的每一个元素。但是,需要注意的是,空桶两侧的非空桶差值不一定就是最大的差值。比如:桶10-19内只有值为19的元素,桶20-29为空,桶30-39内只有值为30的元素,桶40-49内只有值为49的元素,此时很明显最大差值不来自于空桶两边的非空桶。故在遍历桶的时候,遇到空桶的时候,就跳过;如果是非空桶,就寻找下一个非空桶,记录其差值,并与最大差值进行比较,遍历完即可。

代码实现:

package com.gxu.dawnlab_algorithm1$2;
/**
 * 给定一个数组,求如果排序之后,相邻两数的最大差值,要求时间复杂度O(N),且要求不能用非基于比较的排序。
 * @author junbin
 *
 * 2019年4月12日
 */
public class MaxGap {
	public static int maxGap(int[] arr){
		if(arr == null || arr.length < 2){
			return 0;
		}
		int len = arr.length;
		int min = Integer.MAX_VALUE; 
		int max = Integer.MIN_VALUE;
		for (int i = 0; i < arr.length; i++) {
			min = Math.min(min, arr[i]); 
			max = Math.max(max, arr[i]);
		}
		if(min == max){
			return 0;
		}
		
		boolean[] hasNum = new boolean[len+1];
		int[] maxs = new int[len+1];
		int[] mins = new int[len+1];
		int bid = 0; //几号桶
		
		//分桶并对比
		for (int i = 0; i < len; i++) {
			bid = bucket(arr[i],len,min,max);//确定当前数属于几号桶
			mins[bid] = hasNum[bid]?Math.min(mins[bid], arr[i]):arr[i];//更新该桶最小值
			maxs[bid] = hasNum[bid]?Math.max(maxs[bid], arr[i]):arr[i];//更新该桶最小值
			hasNum[bid] = true;
		}
		
		int res = 0;//最终结果
		int lastMax = maxs[0];//距离当前桶左边最近的非空桶
		for(int i = 1;i<len+1;i++){
			if(hasNum[i]){
				res = Math.max(res, mins[i]-lastMax); //当前桶最小值减去左边最近非空桶的最大值
				lastMax = maxs[i];
			}
		}
		return res;
		
	}
	
	public static int bucket(int num,int len,int min,int max){
		return (int)(num - min) * len / (max - min);//((num-min)/(max-min))*len
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值