Maximum Gap 典型线性排序
https://leetcode.com/problems/maximum-gap/
Given an unsorted array, find the maximum difference between the successive elements in its sorted form.
Try to solve it in linear time/space.
Return 0 if the array contains less than 2 elements.
You may assume all elements in the array are non-negative integers and fit in the 32-bit signed integer range.
解题思路:
看到题目,几个关键的信息。未排序、线性的时间和空间。
要计算相邻数字的最大gap,还要O(n)时间解决,能想到的就是先排序,然后遍历一遍返回最大值。于是,常见的线性排序,或者说《算法导论》里提到的,计数排序、基数排序和桶排序。
于是先写了下面的计数排序方法,不过是当作每个元素只出现一次对待的,因为题目算的是gap,多个相同的数字完全可以当作一个 数字看待。
public class Solution {
public int maximumGap(int[] num) {
if(num.length < 2) {
return 0;
} int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for(int i = 0; i < num.length; i++) {
min = Math.min(min, num[i]);
max = Math.max(max, num[i]);
} int[] numSort = new int[max - min + 1];
for(int i = 0; i < num.length; i++) {
numSort[num[i] - min] = num[i];
} int maxGap = 1;
int pre = numSort[0], cur = numSort[0];
for(int i = 1; i < numSort.length; i++) {
if(numSort[i] == 1) {
cur = numSort[i];
maxGap = Math.max(maxGap, cur - pre);
pre = cur;
}
} return maxGap;
}
}
结果遇到[2,99999999]这个例子的时候,内存溢出了,因为在创建那个numSort数组的时候,太大。
其实上面的计数排序就是一个鸽笼排序(Pigeonhole sort),即每个数字只会出现一次的计数排序。标准的计数排序中,记录每个数字出现次数的数组,大小也是必须为max - min + 1的,因为你没法知道在他们中间有多少数字。所以说,用计数排序,这个问题无法避免。只能另寻方法。
既然是因为两个数字相差太大,实际上并没有必要用那么大的数组,那么能不能以最小数字min为基础,用num[i]和min的差去除以平均的差,来确定当前数字可能在位置?
其实这就是桶排序。上面说了,计数排序最好是,所有数字在一个比较小的范围内。那么桶排序也要一个前提,就是所有数字最好尽量随机分散,不能太聚合。这样,他们才能被分散在每个桶里面,每个桶只有很少的数字,甚至0或1个,这个算法效率最高。
思路出来了,但是还有几个细节需要考虑。
1. 这个平均差(也就是桶的大小bucketSize),如何取?
对于一个有n个数字的数组,我们先取出最大数max,最小数min。因为总共有n - 1个gap,所以平均差为(max - min) / (n - 1)。注意,这个数字应该是一个精确值,也就是一个double。为什么?因为平均差可能是0.4,比如数字很多,但是相差却很小。为了避免平均差为0,这里必须取精确的double,或者对(max - min) / (n - 1)取ceil,也就是向上取整。注意不能取floor,否则平均差《1的时候,就变成0了。
2. 根据平均差,每个数字所在桶的下标如何计算?
每个数字的下标必须以min为底计算,(num[i] - min) / bucketSize,再对它取floor。这样,把每个数字向下归到了每个桶里。注意,在这里用ceil也是可以的,也就是向上归到每个桶里!
这样,min的下标为0,max的下标为n - 1。桶的大小和原数组一样,为n。
3. 最大gap如何计算?
因为有n个数字,n个桶。假设每个数字平均分在每个桶里。那么我们只需要逐一计算各个桶里唯一数字的差,返回最大值即可。
假设有一个或多个桶里的数字>1个,那么必然有一个桶里没数字,那么最大差一定在这个空桶周围发生,一定不在那个有多个数字的桶里。所以这时,我们只需要维护每个桶内数字的最大值和最小值。然后,用前一个桶的最大值和后一个桶的最小值比较,返回最大差即可。
回想第一种情况,在每个桶里有且仅有一个数字的情况,我们只要将最大值和最小值都设置为这个数值,然后用同样的方法计算,既可以返回最大差了。
代码如下。
public class Solution {
public int maximumGap(int[] num) {
if(num.length < 2) {
return 0;
}
if(num.length == 2) {
return Math.abs(num[0] - num[1]);
} int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for(int i = 0; i < num.length; i++) {
min = Math.min(min, num[i]);
max = Math.max(max, num[i]);
} double bucketSize = (double)(max - min) / (num.length - 1);
int[][] buckets = new int[num.length][2];
for(int i = 0; i < num.length; i++) {
int k = (int)Math.floor((double)(num[i] - min) / bucketSize);
if(buckets[k][0] == 0) {
buckets[k][0] = num[i];
} else {
buckets[k][0] = Math.min(buckets[k][0], num[i]);
}
if(buckets[k][1] == 0) {
buckets[k][1] = num[i];
} else {
buckets[k][1] = Math.max(buckets[k][1], num[i]);
}
} int maxGap = 0;
int preMax = 0;
for(int i = 0; i < buckets.length; i++) {
if(buckets[i][0] == 0 && buckets[i][1] == 0) {
continue;
}
if(preMax == 0) {
preMax = buckets[i][1];
continue;
}
maxGap = Math.max(maxGap, buckets[i][0] - preMax);
preMax = buckets[i][1];
} return maxGap;
}
}
bucketSize用ceil去做:
public class Solution {
public int maximumGap(int[] num) {
if(num.length < 2) {
return 0;
}
if(num.length == 2) {
return Math.abs(num[0] - num[1]);
} int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for(int i = 0; i < num.length; i++) {
min = Math.min(min, num[i]);
max = Math.max(max, num[i]);
} int bucketSize = (int)Math.ceil((double)(max - min)/(num.length - 1));
// 或者下面的方法也可以
// int bucketSize = (max - min)/num.length+ 1;
int[][] buckets = new int[num.length][2];
for(int i = 0; i < num.length; i++) {
int k = (num[i] - min) / bucketSize;
if(buckets[k][0] == 0) {
buckets[k][0] = num[i];
} else {
buckets[k][0] = Math.min(buckets[k][0], num[i]);
}
if(buckets[k][1] == 0) {
buckets[k][1] = num[i];
} else {
buckets[k][1] = Math.max(buckets[k][1], num[i]);
}
} int maxGap = 0;
int preMax = 0;
for(int i = 0; i < buckets.length; i++) {
if(buckets[i][0] == 0 && buckets[i][1] == 0) {
continue;
}
if(preMax == 0) {
preMax = buckets[i][1];
continue;
}
maxGap = Math.max(maxGap, buckets[i][0] - preMax);
preMax = buckets[i][1];
} return maxGap;
}
}
总结一下,这道题的考点主要在线性时间的几种算法,前面也总结了他们的优劣和适用情况。实际上计数排序常被作为基数排序的一个子方法来采用,也就是对某一位数字进行排序的时候,因为某一位数字的区间只有[0, 9],符合计数排序范围不大的前提。
在桶排序的时候,每个桶的内部排序常常使用插入排序,因为我们假定采用桶排序的数字,是比较分散的,这样落在每个桶里的数字就比较少。在数字比较少的时候,插入排序是比较快的。桶排序的平均时间复杂度为O(n),最坏也可能达到O(n^2),这取决于每个桶内的排序方法。但是,就像《算法导论》上面说的那样,桶排序的时间复杂度的数学预期是O(n)。
回到这道题目,我们看到因为要求的是max gap,所以无需对每个桶内部进行排序,只要维护每个桶各自的最大值和最小值即可。
最后,这道题桶排序的bucket size,bucket的大小,以及如何取bucket的下标,都是需要注意小心操作的。所谓talk is cheap, show me the code,即使知道思路,能写出bug-free的code也是不容易的。
最后给一个mcgill大学的课件,和本题很像。他去除了最大值和最小值,用抽屉原理实现推导,思路也是差不多。
http://cgm.cs.mcgill.ca/~godfried/teaching/dm-reading-assignments/Maximum-Gap-Problem.pdf
另外还有一个介绍桶排序的文章:http://hxraid.iteye.com/blog/647759
Maximum Gap 典型线性排序的更多相关文章
- 线性时间的排序算法--桶排序(以leetcode164. Maximum Gap为例讲解)
前言 在比较排序的算法中,快速排序的性能最佳,时间复杂度是O(N*logN).因此,在使用比较排序时,时间复杂度的下限就是O(N*logN).而桶排序的时间复杂度是O(N+C),因为它的实现并不是基于 ...
- 【leetcode 桶排序】Maximum Gap
1.题目 Given an unsorted array, find the maximum difference between the successive elements in its sor ...
- 由Maximum Gap,对话桶排序,基数排序和统计排序
一些非比较排序 在LeetCode中有个题目叫Maximum Gap.是求一个非排序的正数数列中按顺序排列后的最大间隔.这个题用桶排序和基数排序都能够实现.以下说一下桶排序.基数排序和计数排序这三种非 ...
- leetcode[164] Maximum Gap
梅西刚梅开二度,我也记一题. 在一个没排序的数组里,找出排序后的相邻数字的最大差值. 要求用线性时间和空间. 如果用nlgn的话,直接排序然后判断就可以了.so easy class Solution ...
- LeetCode 164. Maximum Gap[翻译]
164. Maximum Gap 164. 最大间隔 Given an unsorted array, find the maximum difference between the successi ...
- 【LeetCode】164. Maximum Gap (2 solutions)
Maximum Gap Given an unsorted array, find the maximum difference between the successive elements in ...
- 【刷题-LeetCode】164 Maximum Gap
Maximum Gap Given an unsorted array, find the maximum difference between the successive elements in ...
- 【leetcode】Maximum Gap
Maximum Gap Given an unsorted array, find the maximum difference between the successive elements in ...
- [LintCode] Maximum Gap 求最大间距
Given an unsorted array, find the maximum difference between the successive elements in its sorted f ...
随机推荐
- python后端开发工程师考证试题
python开发工程师考证试题 问答题链接 python开发工程师考证试题 选择题 题目 关于 Python 程序格式框架的描述,以下选项中错误的是 ( A ) A: Python 语言不采用严格的“ ...
- Leetcode 204计数质数
计数质数 统计所有小于非负整数 n 的质数的数量. 示例: 输入: 10 输出: 4 解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 . 比计算少n中素数的个数. 素数又称质 ...
- noip模拟赛 无题
分析:这道题和以前做过的模拟赛题很像:传送门. 对于前30%的数据可以直接暴力求,k=1的数据利用线段树求区间最大值,没有修改操作可以用主席树.100%的数据主席树是肯定用不了的,观察到K非常小,可以 ...
- CF901C. Bipartite Segments
n<=300000,m<=300000的图,图上只有奇环,q<=300000个询问每次问:一个区间内有多少个子区间,满足只保留编号在该区间的点以及他们之间的边,可以构成一个二分图. ...
- ****HTML模板资源汇总
站长素材: http://sc.chinaz.com/tag_moban/HTML.html wordpress模板: http://www.cssmoban.com/wpthemes/ http:/ ...
- Layui弹出层、日期和时间选择、即时通讯、分页
Layui弹出层.日期和时间选择.即时通讯.分页 弹层组件文档 - layui.layer 对于弹出层的感觉是:layer 至今仍作为 layui 的代表作,她的受众广泛并非偶然,而是这数年来的坚持. ...
- JVM(四):深入分析Java字节码-下
JVM(四):深入分析Java字节码-下 在上文中,我们讲解了 Class 文件中的文件标识,常量池等内容.在本文中,我们就详细说一下剩下的指令集内容,阐述其分别代表了什么含义,以及 JVM 团队这样 ...
- MySQL查询去重语句
1.distinct select count(distinct CName) from Course select count(CName) from (select distinct CName ...
- JDBC基础教程:tutorialspoint-jdbc
来自turorialspoint的JDBC基础教程(英文),官网:https://www.tutorialspoint.com/jdbc/index.htm 这个教程在国内已经被翻译成中文(不过是属于 ...
- kill杀死进程方法
查找进程:ps -ef | grep firefox kill -s 9 1827