数据结构之——找到无序数组中排序后相邻元素差值的最大值

/**
 * 有一个整形数组A,请设计一个复杂度为O(n)的算法,算出排序后相邻两数的最大差值。
给定一个int数组A和A的大小n,请返回最大的差值。保证数组元素多于1个。
 * @author Administrator
 *

 */

今天在牛客网刷题时,遇到这么一道题,听完知识点讲解后,稍微有些不理解,后来自己又仔细想了想。现在整理下思路。


这个题中,数组是乱序的,所以很容易想到的解法就是先选择一种排序算法,然后依次迭代找到最小值即可。但是这样的复杂度就依赖与排序算法的复杂度了。若是采用计数排序的话,有些不现实,因为最大值和最小值之间可能会差别比较大,造成空间浪费。


其实说这么多,就是为了说明这个题还有一种解法,就是类似桶排序的算法。

对于 数组中 n 个数,我们用 n+1 个桶来装,而且在构造这n+1个桶的时候,,需要一点技巧。我们把 用 n (注意:此处用 n ,而不是 n+1 )来除 max{A} - min{A} ,这样就把数组中 从最大值到最小值 分成了 n 份,这儿稍微有点绕。可以参考下面的图片进行理解。一开始还以为老师讲错了,后来debug 了下,发现还真是这么回事。利用下面的代码计算每个元素的桶号,这样计算出来的最大元素的桶号正好是最后一桶。 num 表示的桶的数目, 即 n+1  ,value表示当前要入桶的值。

[java]  view plain  copy
  1. //该方法用来计算对应元素的应该放入的桶号  
  2.     private int bucket(long value, long num, long max, long min) {  
  3. //      int tag = (int) ((value-min)/((max-min)/(num-1)));  应该避免这种连续除法的表达式,因为每次处的时候都会舍,到最后结果会相差很大  
  4.         int tag = (int) ((value-min)*(num-1)/(max-min));  
  5.         return tag;  
  6.     }  

可以这么理解,我们把这些元素想象成人,元素值的大小就是人的身高,然后根据每个人的身高,决定某个人应该站在哪一级台阶上。站台阶的规则是这样的:最矮的只能站在地上,最高的则站在最高处。因为一共有 n 个数, 但是却有 n+1 个台阶,所以,中间必然有个台阶是没“人” 站的,当然,可能还有另外的台阶也没站人,但是,此时至少有一个是没有站人的,至少有一个,抽屉原理。,而且,而且,(预警,问题的关键点来了)相邻台阶上的 “人” 的身高的差,不会大于中间隔着空闲台阶的人的身高差!道理很简单,反证一下,如果相邻台阶上人的身高差,超过一个台阶高度的话,那么这两个人站上的台阶就不可能是挨着的。所以,最大的身高差,只能出现在与空闲台阶挨着的左右两个非空闲台阶中。


好,问题说到这儿,说了一半。另外一个是:我们没有必要将每个元素到放到对应的台阶上,每个台阶上只留下两个值就好,一个最大值,一个最小值。开始的时候,台阶上的最大值和最小值可以都初始化为零。然后更新呢就好。


好,那么问题到这儿就可以做个了结了。两个相邻元素的最大的差值,就是,空闲台阶的后一个紧挨着的非空闲台阶上的最小值     减去     空间台阶上前一个紧挨着的非空闲台阶上的最大值。注意,不一定只有一个空闲台阶,所以要循环迭代,更新最大的gap。


上图:

只是一个简单的例子供理解使用。


其实这个题可以没必要非得想象成台阶,但是为了说明问题,不防这么思考下。因为人的思维总是不擅长抽象的东西,而更喜欢具象的东西。我们何不利用这个特点呢?其实,数据结构中很多地方都把抽象的东西具象化了。比如,根据最小堆排序,最小堆无非就是我们对数组中的元素进行来来回回交换的一个准则而已,如果写程序的过程中,头脑中不想象出一个堆的话,恐怕会很难写吧。


最后还想啰嗦一下。我写博客主要是为了给自己看的,方便自己后来复习。当哪天发现知识点有些遗忘的时候,打开自己整理过的博客读读,应该很快就能重拾记忆。但是,我在翻阅别人博客的时候,发现有些人的博客好像就是要专门写给别人看的一样,字里行间透漏着 我给你讲。。。。。你应该。。。。怎样怎样,,什么什么 你懂吗?。。。 啊,这个这个你竟然都不懂?。。。。。。。一副好为人师的样子。自己永远都是那么牛逼样子。谁都有一个从认识到理解的过程啊,你也不是一下子就那么牛逼的不是?看的我好生厌恶。借此吐槽一下。

[java]  view plain  copy
  1. package Sort;  
  2. /** 
  3.  * 有一个整形数组A,请设计一个复杂度为O(n)的算法,算出排序后相邻两数的最大差值。 
  4.     给定一个int数组A和A的大小n,请返回最大的差值。保证数组元素多于1个。 
  5.  * @author Administrator 
  6.  * 
  7.  */  
  8. public class Gap {  
  9.     public int maxGap(int[] A, int n) {  
  10.           
  11.         if( A==null || A.length<2){   //感觉这些代码总是忘写,其实还是很有用的,只能说明思维不严谨啊  
  12.             return 0;  
  13.         }  
  14.         int max = A[0];  
  15.         int min = A[0];  
  16.         //找到最大值和最小值  
  17.         for(int i=0;i
  18.             max = Math.max(max, A[i]);  
  19.             min = Math.min(min,A[i]);  
  20.         }  
  21.         if(max==min){           //感觉这些代码总是忘写,其实还是很有用的  
  22.             return 0;  
  23.         }  
  24.         int num =  n+1;//共用n+1个桶来存放,其中大的元素必定放在最后一个桶中  
  25.         int[] mins = new int[num];//存放每个桶中的最小值,在向桶中放入元素时,更新此值  
  26.         int[] maxs = new int[num];  
  27.         boolean[] hasNum = new boolean[num]; //用来标记对应下标的桶中是否为空  
  28.           
  29.         //向桶中放入元素,每次放入元素时,根据原桶中的状态来更新  
  30.         for(int i=0;i
  31.             int tag = bucket(A[i],num,max,min);  
  32.             mins[tag] = hasNum[tag]?Math.min(mins[tag], A[i]):A[i];  
  33.             maxs[tag] = hasNum[tag]?Math.max(maxs[tag], A[i]):A[i];  
  34.             hasNum[tag] = true;  
  35.         }  
  36.   
  37. //      放好元素后,开始计算最大的差值  
  38.         int gap = 0;  
  39.         int preMax = 0;//记录前一个桶中的最大值  
  40.         int aftMin = 0;//记录后一个桶中的最小值   
  41.         int i = 0;  
  42.         while(i//在n+1 个桶中循环更新 gap的值,每次只在空桶两侧更新,因为首先n个元素,n+1个桶,保证了肯定有空桶,并且最大的差值肯定出现在空桶的两侧非空桶中元素的差值中  
  43.             while(i//寻找第一个空桶,因为n+1 个桶存放 n 个元素,肯定会有空桶存在,而最大值,就会出现在空桶中相邻两个桶中元素的差值  
  44.                 i++;  
  45.             }  
  46.             if(i>=num)  
  47.                 break;  
  48.             preMax = maxs[--i];  
  49.             i++;  
  50.             while(i//在上次找到了空桶的基础上,继续循环,空桶之后的下一个非空的桶,此处因为我们把最大的元素放到了最后一个桶中,所以不用担找不到下一个非空的桶  
  51.                 i++;  
  52.             }  
  53.             aftMin = mins[i];     
  54.             gap = gap>(aftMin  - preMax)?gap:(aftMin  - preMax);   
  55.         }  
  56.         return gap;  
  57.     }  
  58. //该方法用来计算对应元素的应该放入的桶号  
  59.     private int bucket(long value, long num, long max, long min) {  
  60. //      int tag = (int) ((value-min)/((max-min)/(num-1)));  应该避免这种连续除法的表达式,因为每次处的时候都会舍,到最后结果会相差很大  
  61.         int tag = (int) ((value-min)*(num-1)/(max-min));  
  62.         return tag;  
  63.     }  
  64.     public static void main(String[] args) {  
  65.         Gap g = new Gap();  
  66.         //int[] a = {9590,5695,4776,5450,9128,2219,4782,5023,3685,54,2688,4716,8175,1012,3079,5574,6336,3755,5328,2925,3825,3443,9253,7427,3809,9360,8809,5885,8944,2048,8170,1285,1223,2186,2969,5342,5076,911,391,1950,2480,1264,9795,45,1763,9169,7526,6225,5571,5805,9468,9383,6885,1576,9810,6544,853,9628,175,4595,5025,8688,9239,975,5370,673,9435,5559,205,744,2146,2597,987,7727,3116,5843,1575,6252,2245,4205,3481,3600,1910,8912,3824,1333,8392,6311,3504,8379,9636,244,5835,1896,4181,5452,617,4850,6326,2381,4524,5502,9970,9919,8138,69,5831,7087,416,8547,5568,4658,7418,2703,837,9223,7926,5703,3623,7347,8297,5270,9026,1913,6588,4362,6278,2129,422,6819,3828,7056,3926,8088,7424,7823,9408,2836,4629,8770,5793,662,269,9958,9569,9321,5095,9075,9883,8858,79,7148,6446,2854,8157,9279,6063,696,236,7060,8070,3431,854,4811,858,2501,9900,6987,5021,6246,8057,3198,2847,2758,4540,5268,7924,6729,8661,9045,8118,6669,5603,5585,9753,8098,2712,6487,2500,7702,7103,9492,7662,7623,671,7095,2214,7512,4991,1712,5238,1874,8203,6442,6785,158,1343,2574,415,6316,8487,3159,2151,5176,3250,1572,7555,188,3098,5595,7337,5693,2928,5179,1251,8417,1235,7807,1733,4618,1664,5007,1589,857,4440,4041,4652,6515,1284,6564,8916,9961,7111,4749,6903,9085,4064,6868,1399,9619,4977,4505,5226,7264,5149,9909,5065,1164,3057,9360,5084,336,4994,9715,7446,8395,7324,6319,9460,464,2259,2183,2365,9115,7768,8529,4445,2349,3414,5595,2768,9367,9366,8874,2052,8875,8481,8528,8631,874,3344,2434,2454,4231,5793,5388,9261,6075,4236,904,3433,7027,7065,3230,8830,6011,830,9467,8512,3098,6858,7226,228,4202,7216,2414,9676,8528,348,7004,8965,7050,4045,5379,9432,6224,8699,3739,9822,1867,1486,9600,6690,930,2807,7584,691,8949,118,9720,1286,3489,5623,1596,8657,7902,63,5136,4851,1111,5665,4060,9291,5156,4845,5331,8544,6089,6574,5618,4406,6850,939,5107,9179,2240,1964,7938,1021,6793,8417,1102,2565,8379,703,7768,8183,5518,9335,3839,1258,8463,589,5209,2419,9296,3570,9351,2648,4734,6006,306,5720,1112,8299,1619,68,1986,279,7750,9680,9980,5407,7364,1275,4493,3118,4026,2670,2072,978,9299,3314,2361,4415,9950,8859,5163,817,4150,7022,891,4788,7405,9288,5527,3923};  
  67.         int[] a = {3,1,2,4,9,8,7};  
  68.         int c = g.maxGap(a, 7);  
  69.         System.out.println(c);  
  70.     }  
  71. }  

转载地址:

http://blog.csdn.net/Leo____Wang/article/details/53367723

你可能感兴趣的:(数据结构)