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 ...
随机推荐
- selenium--driver.switchTo()-----转
https://www.cnblogs.com/clairejing/p/9499223.html 在自动化测试中,会遇到多窗口.多iframe.多alert的情况.此时,会使用driver.swit ...
- stm32 调试时卡在LDR R0, =SystemInit
网上找到的可能的原因 堆栈空间默认的太小 默认startup_stm32f10x_hd.s中 Stack_Size EQU 0x00000400,如果改大之后,可能调试就可以正常运行. 出现最多的情况 ...
- 图论算法——最短路径Dijkstra,Floyd,Bellman Ford
算法名称 适用范围 算法过程 Dijkstra 无负权 从s开始,选择尚未完成的点中,distance最小的点,对其所有边进行松弛:直到所有结点都已完成 Bellman-Ford 可用有负权 依次对所 ...
- hihoCoder#1042 跑马圈地
原题地址 经网友jokeren提醒,后面给出的代码虽然可以AC原题,但存在bug,主要是在矩形覆盖情况的判断上处理的不够完全. 看似挺复杂的,但是仔细分析一下可以化简: 首先,不用枚举周长,因为更长的 ...
- hihoCoder#1094 Lost in the City
原题地址 限时10s,所以不用考虑什么算法了,暴力吧 分别按照3x3视野的四个方向去地图上匹配,把符合的地点标记出来,最后统一按照从上到下,从左到右的顺序输出. 代码: #include <io ...
- android开发里跳过的坑——onActivityResult在启动另一个activity的时候马上回调
该问题是由于被启动的activity的launchMode为singleTask模式,该模式下不可以使用onActivityResult,要使用onActivityResult,被启动的activit ...
- intent使用Serializable传递对象
package com.pingyijinren.test; import android.content.Intent; import android.support.v7.app.AppCompa ...
- Codeforces 629D Babaei and Birthday Cake(树状数组优化dp)
题意: 线段树做法 分析: 因为每次都是在当前位置的前缀区间查询最大值,所以可以直接用树状数组优化.比线段树快了12ms~ 代码: #include<cstdio> #include< ...
- Delphi与Windows 7下的用户账户控制(UAC)机制
WIN7, Vista提供的UAC机制,它的主要目的是防止对于操作系统本身的恶意修改.对于Delphi程序的影响,UAC主要在于以下几点:1.由于UAC机制,Delphi对于系统的操作可能无声的失败, ...
- linux 硬件中断调节
什么是中断 中断interrupts是指硬件主动的来告诉CPU去做某些事情.比如网卡收到数据后可能主动的告诉CPU来处理自己接受到的数据,键盘有了按键输入后会主动告知CPU来读取输入. 硬件主动的打扰 ...