hello,今天给大家带来一道算法题。这道算法题,是我目前为止,见过最难的一道题。那么到底是怎样的一道算法题呢?如下:

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

我查了一下,暂时没有找到一个在线OJ的链接,只能自己写一个对数器,手动测试了。

当初我看到这个题目的时候,说这怎么可能呢?在一个无序的数组中,求相邻两个数据的最大差值。可是我们都知道,现在基于比较的排序算法,最快也只能够达到O(N*logN)的水平,而题目明确限制时间复杂度要是O(N),所以想通过基于比较的排序,排序之后再进行遍历,时间复杂度肯定是达不到要求的。

有人可能也会想说,不是还有基于非比较的排序算法吗?比如计数排序、基数排序、桶排序。但是题目又明确规定了不能使用基于非比较的排序算法。

这样的话,想使用排序算法,进行排序,这条路肯定是行不通的。只能另外想其他的办法。

-------------------------------------------------------------------------------------------我是分割线-----------------------------------------------------------------------------------------

重头戏来了!!!整个代码的流程如下:

  1. 先遍历一遍数组,保存整个数组的最大值和最小值。

  2. 假设数组中一共有N个元素,那么我们就需要准备N+1个桶。

    这每一个桶里面,可以存储一定范围内的数值,而具体可以存储多大范围内的数值,需要用公式去计算。比如:第一个桶存储0……9之间的数,第二个桶存储10……19的数……

  3. 我们再次遍历一遍数组,将每一个数,放入到相应的桶里。

解释:为什么需要进行以上这3个步骤???这是一个非常值得思考的问题!!!

由题可知,一共有N个数,但是我们准备了N+1个桶。也就是说我们将每个数放入相应的桶中,就算这N个数都在各自的桶里,无论怎么放入,始终会多出来1个空桶

而我们会根据一下这个公式,将每个数放入相应的桶:(arr【i】- min)* N / (max - min)。

以上这个公式,就能够计算出i位置的数,应该放入哪一个桶里。根据公式计算,最小值一定会放到第一个桶里,最大值也一定会放到最后一个桶里。那么既然第一个和最后一个桶肯定是有数据的,也就是说明那个空桶肯定是中间的某一个桶

正是因为这个空桶的存在,会将很多种计算的可能性直接抹杀掉。说的具体点,假设一个桶的存储数的范围是0~9,也就是这个桶能够存储10个数,既然有一个空桶的话,那么肯定最后的答案是大于10的。

那么既然大于10的话,这两个数肯定不会在同一个桶里。这样的话,我们就排除了桶里面两个数据的情况,只需要考虑相邻两个桶之间的数,才可能是最终的答案。

就如上图的形式,将所有的数据都放入相应的桶里。因为有空桶的存在,所以我们的答案必然是在两个不同桶之间的数据进行相减。而我们在进行相减的时候,只需要记录每个桶的最大值和最小值即可。也就是说,用后一个桶的最小值,减前一个桶的最大值。以这样的形式,循环N次,将每两个相邻的桶进行计算,就能得到最终的答案。

既然我们只需要每个桶里的最大值和最小值,那就准备两个数组maxs和mins,分别存储即可。代码如下:

以上就是这道题的所有代码,代码不多,但是其中的算法思想我觉得真的是很厉害,很难想象出,想到这个方法的是什么人。

核心就在于那个空桶的存在,抹杀很多的可能性。使其最终的答案只可能存在于相邻两个桶之间的数。

提问:假设给定的某一个数组,算出来桶的数据后,只有一个是空桶。那么最终的答案就一定是这个空桶右边桶的数据减去左边桶的数据吗?

最后,我将整个代码全部放到下面,包括了一个对数器,用于测试以上代码的正确性。

import java.util.Arrays;public class Code01_CalcTwoNumDiv {public static void main(String[] args) {int testTime = 5000; //测试次数int N = 50; //数组长度int range = 1000; //数据范围boolean flag = true;for (int i = 0; i < testTime; i++) {int[] arr = generateArr(N, range);int p1 = calcTwoNumDiv(arr);int p2 = sortAfter(arr);if (p1 != p2) {flag = false;break;}}System.out.println(flag? "正确" : "错误");}public static int calcTwoNumDiv(int[] array) {if (array == null || array.length < 2) {return 0;}int max = Integer.MIN_VALUE;int min = Integer.MAX_VALUE;for (int i : array) { //先遍历一遍数组,求最大值最小值max = Math.max(max, i);min = Math.min(min, i);}if (max == min) {return 0; //如果最大值和最小值相等,说明这个数组只有这一个数据}int len = array.length;boolean[] hasNum = new boolean[len + 1];int[] maxs = new int[len + 1];int[] mins = new int[len + 1];//遍历数据for (int i = 0; i < array.length; i++) {int bit = getBit(array[i], len, max, min); //桶的位置maxs[bit] = hasNum[bit] ? Math.max(maxs[bit], array[i]) : array[i]; //更新最大值mins[bit] = hasNum[bit] ? Math.min(mins[bit], array[i]) : array[i]; //更新最小值hasNum[bit] = true; //始终更新为true}//第一个桶和最后一个桶,肯定是有数据的。int preMax = maxs[0];int res = Integer.MIN_VALUE; //最终的结果for (int i = 1; i <= len; i++) {if (hasNum[i]) {res = Math.max(res, mins[i] - preMax);preMax = maxs[i]; //更新前一个的最大值}}return res;}public static int getBit(int num, int len, int max, int min) {return ((num - min) * len) / (max - min); //计算num应该存储在哪个桶}public static int sortAfter(int[] arr) {if (arr == null || arr.length < 2) {return 0;}Arrays.sort(arr);int res = Integer.MIN_VALUE;for (int i = 1; i < arr.length; i++) {res = Math.max(res, arr[i] - arr[i - 1]);}return res;}public static int[] generateArr(int N , int range) {int[] arr = new int[N];for (int i = 0; i < N; i++) {arr[i] = (int)(Math.random() * range);}return arr;}
}

好啦,本期更新就到此结束啊!!!我们下期见吧!!!

截止目前为止,我遇到的最难的一道算法题:计算相邻两个数的最大差值相关推荐

  1. 谁喝水谁养斑马答案C语言,余式厚:最难的一道推理题叫“谁养斑马”--文化--人民网...

    余式厚的课上得生动是有名的,让枯燥变得有趣是他的拿手好戏 在他的逻辑课上可以唱歌 几年前,余式厚偶遇一位学生,学生考他:"您还能叫出我的名字吗?"余式厚顺利接招,准确无误. 那时, ...

  2. LeetCode上最难的链表算法题,没有之一

    作者 | 程序员小吴 转载自五分钟学算法(ID: CXYxiaowu) 该题在 LeetCode 官网上有关于链表的问题中标注为最难的一道题目:难度为 Hard ,通过率在链表 Hard 级别目前最低 ...

  3. 【原创分析帖】据说Google内部有史以来最难的一道面试题

    逛技术平台的时候,刷到一道算法题,一眼看去,就被其开头吸引了: 摘自知乎某 Google 分布式大神的一道题,技术是Google内部出的有史以来最难的一道题 嗯,距离下班还有一段时间,就看看把. 题目 ...

  4. c语言两字符串转数字后相加,一个觉得很难的C语言问题。对两个数字字符串相加。 C语言 如何把一个字符串中相连的两个数字转化为一......

    导航:网站首页 > 一个觉得很难的C语言问题.对两个数字字符串相加. C语言 如何把一个字符串中相连的两个数字转化为一... 一个觉得很难的C语言问题.对两个数字字符串相加. C语言 如何把一个 ...

  5. 为什么字节跳动的前端面试需要那么难的算法题?

    首先我来辟个谣: 随便打开一个招聘网站,你会发现前端工程师的岗位需求依旧庞大,大厂人才奇缺,就业薪资起点高,无行业限制. (数据来源:职友集) 前端开发的行业大环境 行业升级,如果说以前只会HTML. ...

  6. CSDN截止目前为止有多少的博客专家以及比例人数?

    今天比较好奇,网上搜索了一下CSDN博客专家数目,搜索不出来:于是就在CSDN博客里面查找,终于在这个链接地址找到了.截止到2017年3月8日,全网总的博客专家是:1387人.记得2011年CSDN数 ...

  7. 【网申投递汇总】截止目前为止,还可以网申的公司 持续更新

    作者:筱茜 链接:www.nowcoder.com/discuss/536- 来源:牛客网 这是一个汇总贴,不一定全,大家可以在帖子下留言告诉我还有哪些可以继续投递 本帖依然持续更新,记得收藏点赞=. ...

  8. 史上最难的一道Java面试题

    点击上方"架构师小秘圈",选择"置顶公众号" 第一时间关注架构师方面的技术干货 无意中了解到如下题目,觉得蛮好. 题目如下 public class TestS ...

  9. HDU2549 壮志难酬【水题+输入输出】

    壮志难酬 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

最新文章

  1. Kruskal算法构造最小生成树
  2. matlab gui七种参数传递方式和范围
  3. 《明解C语言》pdf
  4. (转)自定义EndNote的输出样式(output style)
  5. POJ 2106 Boolean Expressions (布尔表达式求值)
  6. 那些你可能还不知道的新发明
  7. SQL表名,应该用复数还是单数
  8. 《解析几何》吕林根,徐子道第四版 习题 1.4.7,1.4.8,1.4.9
  9. 里氏替换原则_春辉带你了解面相对象设计第二原则(里氏替换原则)
  10. python中avg函数的使用_PostgreSQL avg()函数
  11. 如何做系列(5)-james mail安装总结
  12. 数据库系统--期末复习
  13. 1362:家庭问题(family)
  14. python——spilt和strip用法
  15. 解决RuntimeError: cuDNN error: CUDNN_STATUS_EXECUTION_FAILED 踩过的坑(配置环境:linux+anaconda3+pytorch ...)
  16. 看完这套书才发现,以前的四大名著都白看了!
  17. ubuntu 印象笔记
  18. vue使用three.js加载obj和mtl
  19. 第三章数程序设计初步--分支结构项目3利息计算器
  20. 抄代码对自己编程提高有用吗?

热门文章

  1. 构建基于LXR的源代码浏览平台
  2. matlab函数图像代码,matlab图像函数大全
  3. iacr crypto 级别_来了!10款10000元级别公路车推荐
  4. ACM模板 素数打表
  5. Android简单实现搜索功能 显示清除历史搜索记录
  6. 警方通报空姐遇害!滴滴100万悬赏嫌疑司机!请转发找凶手!
  7. AD域服务器的搭建(4)--LADP概述
  8. oracle查询历史会话,Oracle用户会话信息的查询方法
  9. FishEye安装部署
  10. android 7.0安装包,安卓7.0安装包,安卓7.0通用升级安装包免费预约 v7.0-手游汇