题目描述
请设计一个复杂度为O(n)的算法,计算一个未排序数组中排序后相邻元素的最大差值。
给定一个整数数组A和数组的大小n,请返回最大差值。保证数组元素个数大于等于2小于等于500。
测试样例:
[9,3,1,10],4
返回:6
通过的代码
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class Main {
public static void main(String args[])
{
int asdf[]=new int[]{96,614,146,429,487,250,536,392,178,26,569,178,595,129,363,550,26,492,561,435,113,492,44,213,478,590,593,488,72,231,73,190,40,62,448,563,1,582,453,33,538,407,57,214,137,193,253,323,167,511,278,148,581,557,551,126,167,50,438,139,345,203,292,288,233,360,343,308,586,519,495,369,159,126,46,183,482,50,214,264,161,145,504,527,52,578,489,104,269,253,349,464,318,458,96,349,46,563,478,1,395,262,285,46,184,115,146,171,452,223,518,414,33,246,70,286,348,582,338,72,191,58,590,298,538,75,283,459,551,2,41,86,354,58,178,411,321,137,37,41,553,303,380,96,198,125,222,544,97,533,437,610,304,276,98,196,170,240,98,413,328,109,70,270,37,398,377,309,367,86,123,401,480,211,48,506,257,125,137,201,194,188,170,300,49,148,493,439,575,93,588,130,403,220,118,578,419,70,519,558,544,84,594,51,240,68,304,282,27,447,149,486,602,519,261,264,179,362,2,495,564,259,431,162,343,165,86,346,408,559,293,274};
System.out.println(findMaxDivision(asdf, asdf.length));
Arrays.sort(asdf);
System.out.println(Arrays.toString(asdf));
}
public static int findMaxDivision(int[] A, int n)
{
int max=Integer.MIN_VALUE;
int min=Integer.MAX_VALUE;
for(int i=0;i<n;i++)
{
if(max<A[i])
{
max=A[i];
}
if(min>A[i])
{
min=A[i];
}
}
int span=(max-min);
int maxArray[]=new int[n+1];
Arrays.fill(maxArray, Integer.MIN_VALUE);
int minArray[]=new int[n+1];
Arrays.fill(minArray, Integer.MAX_VALUE);
for(int i=0;i<n;i++)
{
int index=(A[i]-min)*n/span;
if(maxArray[index]<A[i])
{
maxArray[index]=A[i];
}
if(minArray[index]>A[i])
{
minArray[index]=A[i];
}
}
int i=1;
int j=0;
int result=0;
while(i<A.length+1)
{
while(i<A.length+1&&minArray[i]==Integer.MAX_VALUE)
{
i++;
}
if(i>=A.length+1)
{
break;
}
while(j<i&&maxArray[j]==Integer.MIN_VALUE)
{
j++;
}
if(j>=i)
{
i++;
continue;
}
if(result<minArray[i]-maxArray[j])
{
result=minArray[i]-maxArray[j];
}
i++;
j++;
}
return result;
}
}
下面是别人给的思路
关键在于:距离平均值为(max-min)/n-1, 则距离最大的数必然大于这个值 这句话
假设有4个数,最小为0.2, 最大为0.8.
那么这四个数的平均距离为(0.8-0.2)/3 = 0.2
则必然存在两个数的差大于等于0.2,即距离最大的两个数必然不会小于0.2.
分成三个桶,分别是0.2~0.4, 0.4~0.6, 0.6~0.8.
那么距离最大的两个数必然不在同一个桶内.
依次比较上一个桶的最大值与下一个桶的最小值的差值,找最大的即可.
这个问题我碰见了一个奇妙的事情。一开始我我程序写的求一个数的索引的方式是这样的
int span=(max-min)/(n-1);//这一行求出每个桶的大小
int maxArray[]=new int[n+1];
Arrays.fill(maxArray, Integer.MIN_VALUE);
int minArray[]=new int[n+1];
Arrays.fill(minArray, Integer.MAX_VALUE);
for(int i=0;i<n;i++)
{
int index=(A[i]-min)/span; //这个可以求出桶的索引
if(maxArray[index]<A[i])
{
maxArray[index]=A[i];
其实个代码看似没有问题可是问起缺非常非常大。
考虑这样的一个测试用例
最大值是614,最小值是1.。数组大小为232.
这样我求出的span是(614-1)/232=2.63
可是因为是int所以变成桶大小是2.因为我桶的总数最多是232个桶。可是对于614这个数(614-1)/2=306。远远超出我最大的桶的大小
这个就是int运算带来的问题。
这个题有两个解决办法。span换成double。可是这样会耽误运算。因为桶也要换成double。。double运算比较复杂
第二个方法是把桶大小运算变成取上整数为3.可是桶大小是3的时候给每个数计算桶索引的时候也很麻烦。看了别人的代码。有了一个绝妙的方法。
int span=(max-min);//这一行求出每个桶的大小
int maxArray[]=new int[n+1];
Arrays.fill(maxArray, Integer.MIN_VALUE);
int minArray[]=new int[n+1];
Arrays.fill(minArray, Integer.MAX_VALUE);
for(int i=0;i<n;i++)
{
int index=(A[i]-min)*n/span; //这个可以求出桶的索引
if(maxArray[index]<A[i])
{
maxArray[index]=A[i];
span是数字的总跨度。然后(A[i]-min)/span肯定是小于1的数字。那么乘以n这样的出来的索引一定是小于n的。。十分美妙的解决方法。值得学习