欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

桶排序思想-最大间隔问题

程序员文章站 2022-03-03 08:23:59
...
import java.util.Arrays;

/**
 * 最大间隔问题。
 * 给定一个数组,求如果排序之后,相邻两数的最大差值,要求时间复杂度O(N),且要求不能用非基于比较的排序
 * 核心在于,构建N+1个桶。n个数肯定放不满,所以一定会有一个空桶。空桶的意义在于否定最大插值在一个桶里面的可能性,这样
 * 才可以通过计算桶间的插值,并选出最大的作为最大差值。
 * @author yitl
 */
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 < len; i++) {
			min = Math.min(min, arr[i]);
			max = Math.max(max, arr[i]);
		}
		if(min == max) {
			return 0;
		}
		
		//构建(len+1)个桶;每个桶只维护三个值,最大值,最小值,和是否有值
		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;
		}
		
		//从1的位置开始遍历,每个桶的最小值都和前面一个桶的最大值比较(空桶会被跳过),选出最大差值
		int res = 0;
		int lastMax = maxs[0];//0号桶一定会有值,因为构建桶时默认放进去最小值。
		int i = 1;
		for(; i<=len; i++) {
			if(hasNum[i]) {
				res = Math.max(res, mins[i] - lastMax);
				lastMax = maxs[i];
			}
		}
		return res;
	}
	
	/**
	 * 给出以下四个值,分完桶以后再给出这个数所在的桶的id
	 * @param num 数组中随机的一个数
	 * @param len 数组的长度
	 * @param min 数组的最小值
	 * @param max 数组的最大值
	 * @return
	 */
	public static int bucket(long num, long len, long min, long max) {
		return (int)((num - min) * len / (max - min));
	}
	
	public static void main(String[] args) {
		System.out.println(System.currentTimeMillis());
		int testTimes = 500000;
		int maxSize = 7;
		int maxValue = 10;
		boolean succeed = true;
		for(int i=0; i<testTimes; i++) {
			int[] arr1 = generateRandomArray(maxSize, maxValue);
			int[] arr2 = copyArray(arr1);
			if(maxGap(arr1) != comparator(arr2)) {
				succeed = false;
				print(arr1);
				break;
			}
		}
		System.out.println(succeed ? "ok" : "no");
		System.out.println(System.currentTimeMillis());
	}
	
	/**
	 * 打印输出
	 * @param arr
	 */
	public static void print(int[] arr) {
		if(arr == null) {
			return;
		}
		for(int i : arr) {
			System.out.print(i+" ");
		}
		System.out.println();
	}
	
	/**
	 * 复制数组
	 * @param arr
	 * @return
	 */
	public static int[] copyArray(int[] arr) {
		if(arr == null) {
			return null;
		}
		int[] res = new int[arr.length];
		for(int i=0; i<arr.length; i++) {
			res[i] = arr[i];
		}
		return res;
	}
	
	/**
	 * 用java自带的排序作为对数器的比较方法。(一般选用绝对正确的,易实现的,不用考虑性能问题)
	 * @param arr
	 */
	public static int comparator(int[] arr) {
		if(arr == null || arr.length < 2) {
			return 0;
		}
		Arrays.sort(arr);
		int gap = Integer.MIN_VALUE;
		for(int i = 1; i < arr.length; i++) {
			gap = Math.max(arr[i] - arr[i-1], gap);
		}
		return gap;
	}
	
	/**
	 * 生成长度随机的数组
	 * 这里对(int)((maxSize+1)*Math.random())进行以下解释:
	 * Math.random() -> double [0,1),假设这里的maxSize=5,size+1=6,
	 * (maxSize+1)*Math.random() -> double [0,6) -> int [0,5]
	 * @param maxSize 数组的长度
	 * @param maxValue 数组的最大值
	 * @return
	 */
	public static int[] generateRandomArray(int maxSize, int maxValue) {
		int[] arr = new int[(int)((maxSize+1)*Math.random())];
		for(int i=0; i<arr.length; i++) {
			arr[i] = (int)((maxValue+1)*Math.random()) - (int)((maxValue)*Math.random());
		}
		return arr;
	}
}