最近在adobe采访中向我提出了这个难题: - 有一个包含数百万个无序正数的数组,其中所有元素都是不同的,除了一个恰好出现两次的数字。动机是以最佳方式找到两次出现的数字。
P.S。绝对没有订单/模式适用于数组。
面试官拒绝了任何形式的可能性,因为这需要花费很多时间,他想要提出质疑,然后提出一个更聪明的解决方案。
答案 0 :(得分:4)
第一种方法是对数组进行排序,然后遍历排序数据,直到找到两个相同的连续数字。这可以在O(n log n)
时间和O(1)
空间轻松完成。
如果面试官随后询问是否有更好的方法,那么您将讨论可能对数据的任何限制(订单/模式不是必然意味着对数据没有任何限制)。你还应该通过最优来质疑他们实际上意味着什么 - 如果没有数量的测量,这个词本身意味着很少。
有些人优化时间,有些人为空间优化,有些人(如我自己)甚至优化代码可读性: - )
在讨论限制方面,一个例子是数字的范围是否限制在几百万。然后,创建一个计数数组并在O(n)
时间处理所有数据将是一件简单的事情:
dim array[several million] as zero
for each number:
array[number]++
if array[number] == 2:
print number
stop
即使没有这样的限制,32位数字范围也可以使用40亿左右的数组(大约500M),这是你经常交易空间的经典例子。
请记住,面试问题不是要弄清楚你是否有针对特定问题的解决方案,他们就是这样,面试官可以看到你的思维过程。通常情况下,你最大的资产不是对算法的百科全书知识,而是你能够聪明地思考问题以及如何解决问题。
答案 1 :(得分:4)
通过将数值散列到集合中的单个顺序传递数组将告诉我重复。这是O(n),但是使用HashSet的内存和数据结构。 Hashing的最坏情况在第一个和最后一个位置是重复的。
甚至高达25M整数的排序很快,约为2秒,并且 - 尽管O(n log n) - 具有相对恒定的时间,并且比最差的散列情况快得多。 OTOH,哈希可以击败排序,以及下一个方法: -
最快的是使用BitMap来记录数字(~1秒),虽然这可能需要相当大的内存量((0x7FFF_FFFF + 1)/ 8 - 即非负整数除以每字节的位数) ,但这里的分配很简单。同样,最糟糕的情况是在第一个和最后一个地方重复。
这是我用来比较的代码。我应该小心谨慎,就像Java中的大多数天真基准一样。但它表明代码可读性不是任何方法的问题。
public class Duplicate {
public static void main(String[] args) throws Exception {
Random r = new Random( 100L );
int[] a = new int[25000000];
Set<Integer> set = new HashSet<>(a.length/2);
boolean dupl = true;
for( int i = 0; i < a.length; ){
int x = Math.abs( r.nextInt() );
if( set.add( x ) ){
a[i++] = x;
}
}
a[a.length-1] = a[0]; // Worst case for HashSet and BitSet
set = null;
System.out.println( "hash " + new Date() );
set = new HashSet<>();
for( int i = 0; i < a.length; ++i ){
if( ! set.add( a[i] ) ){
System.out.println( a[i] );
break;
}
}
set = null;
System.out.println( "bitmap " + new Date() );
BitSet bs = new BitSet( 0x7FFF_FFFF );
for( int i = 0; i < a.length; ++i ){
if( bs.get( a[i]-1 ) ){
System.out.println( a[i] );
break;
}
bs.set( a[i]-1 );
}
System.out.println( "sort " + new Date());
Arrays.sort( a );
for( int i = 1; i < a.length; ++ i ){
if( a[i] == a[i-1] ){
System.out.println( a[i] );
break;
}
}
System.out.println( "done " + new Date() );
}
}
稍后请注意,Java 8具有Arrays.sortParallel。鉴于你有硬件,这将进一步减少排序时间。 - 另请注意,位设置方法基于规范“正数”。如果要包含负数,这会使事情变得复杂,但我怀疑采访者想要了解候选人的“流利性”w.r.t. Java的java.util资源。
答案 2 :(得分:0)
由于数据未排序,您必须根据剩余(n-1)检查每个数字,从而检查O(n ^ 2)。他们要求这种算法的时间复杂度小于O(n ^ 2)。为此,您需要树或哈希表。如果您对该数据进行排序&amp;然后应用任何算法,那将是更耗时的过程。对于树和树哈希表你需要O(n)。因为它们最适合安排数据和找到数据。