Given an unsorted array of integers, find the length of the longest consecutive elements sequence. For example, Given [100, 4, 200, 1, 3, 2], The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4. Your algorithm should run in O(n) complexity.
先排序然后扫描一次,维护一个最长consecutive sequence长度这个方法很直接,但是至少需要O(NlongN). 不让排序,那就要想,估计得用空间来换取时间了。
(CodeGanker的思路)其实这个题看起来是数字处理,排序的问题,但是如果要达到好的时间复杂度,还得从图的角度来考虑。思路是把这些数字看成图的顶点,而边就是他相邻的数字,然后进行深度优先搜索。把数字放到一个集合中,拿到一个数字,就往其两边搜索,看其两边是否在集合中,这样得到包含这个数字的最长串的长度,并且把用过的数字从集合中移除(因为连续的关系,一个数字不会出现在两个串中)。最后比较当前串是不是比当前最大串要长,是则更新。如此继续直到集合为空。如果我们用HashSet来存储数字,则可以认为访问时间是常量的,那么算法需要一次扫描来建立集合,第二次扫描来找出最长串,所以复杂度是O(2*n)=O(n),空间复杂度是集合的大小,即O(n)。
这道题还用了Iterator: 10-11行,不用Iterator可以用HashMap来做,不删,但是设0,1, 0表示访问过
1 public class Solution { 2 public int longestConsecutive(int[] num) { 3 if (num==null || num.length==0) return 0; 4 HashSet<Integer> set = new HashSet<Integer>(); 5 for (int elem : num) { 6 set.add(elem); 7 } 8 int longest = 1; 9 while (!set.isEmpty()) { 10 Iterator iter = set.iterator(); 11 int item = (int)iter.next(); 12 set.remove(item); 13 int len = 1; 14 int i = item - 1; 15 while (set.contains(i)) { 16 len++; 17 set.remove(i--); 18 } 19 i = item + 1; 20 while (set.contains(i)) { 21 len++; 22 set.remove(i++); 23 } 24 if (len > longest) longest = len; 25 } 26 return longest; 27 } 28 }
对Iterator的用法不是太熟悉,比如犯了如下错误:在remove elements的时候java.util.ConcurrentModificationException,这是什么原因呢?容我细细讲解:
javadoc里面对Iterator的叙述是:Iterators differ from enumerations:Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics. Iterator是可以在迭代过程中改变collection结构的,但是!
Some iterators are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. 就是这种Iterator虽然允许在迭代器建立之后、迭代过程中改变collection的结构,但是只能使用它自己定义的remove函数
javadoc里面对java.util.ConcurrentModificationException的叙述是:This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible. For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it. 而且,Note that this exception does not always indicate that an object has been concurrently modified by a different thread. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
错误代码:这个代码与上面代码区别在第9行。该代码在循环外建立迭代器,while循环是迭代器迭代过程,但我们在循环里面用set.remove()函数,而不是迭代器自己的remove函数,就会报这个错。但是iterator.remove() removes from the underlying collection the last element returned by this iterator只能删上一个迭代到的元素,而我们需要在while循环里面套while循环删该元素的上下元素,iterator自己的remove函数达不到要求。
所以就只能采取第一段代码的方法,把iterator的定义放在while循环内,每次都要新建一个iterator,而该iterator不会一直迭代,只负责找当前collection的下一个元素,再用set.remove去删,这样就不冲突了
1 public int longestConsecutive(int[] num) { 2 if (num == null || num.length == 0) return 0; 3 int longest = 1; 4 HashSet<Integer> set = new HashSet<Integer>(); 5 for (int elem : num) { 6 set.add(elem); 7 } 8 Iterator iter = set.iterator(); 9 while (iter.hasNext()) { 10 int item = (int)iter.next(); 11 set.remove(item); 12 int count = 1; 13 int target = item + 1; 14 while (set.contains(target)) { 15 count++; 16 set.remove(target); 17 target++; 18 } 19 target = item - 1; 20 while (set.contains(target)) { 21 count++; 22 set.remove(target); 23 target--; 24 } 25 if (count > longest) longest = count; 26 } 27 return longest; 28 }
把iterator定义放在循环外,不每次新建iterator, 而是使用同一个,一样会报java.util.ConcurrentModificationException错,如:
1 Iterator iter = set.iterator(); 2 while (!set.isEmpty()) { 3 int item = (int)iter.next(); 4 set.remove(item);
另外,最开始提的那个naive方法,可以考虑全局最优和局部最优的方法,注意元素可能重复
1 public class Solution { 2 public int longestConsecutive(int[] num) { 3 if (num == null || num.length == 0) return 0; 4 Arrays.sort(num); 5 int global = 1; 6 int local = 1; 7 int last = num[0]; 8 boolean isAscend = true; 9 boolean isDscend = true; 10 for (int i=1; i<num.length; i++) { 11 if (num[i] == last) continue; 12 if (num[i]==last+1 && isAscend) { 13 local++; 14 isAscend = true; 15 isDscend = false; 16 } 17 else if (num[i]==last-1 && isDscend) { 18 local++; 19 isDscend = true; 20 isAscend = false; 21 } 22 else { 23 local = 1; 24 isAscend = true; 25 isDscend = true; 26 } 27 global = Math.max(local, global); 28 last = num[i]; 29 } 30 return global; 31 } 32 }