算法数据结构面试分享(十一)火眼金睛,从队伍里迅速找出那唯一的一个双胞胎

题目:假设你有一个用1001 个整数组成的数组,这些整数是任意排列的,但是你知道所有的整数都在1 到1000(包括1000)之间。此外,除一个数字出现两次外,其他所有数字只出现一次。假设你只能对这个数组做一次处理,用一种算法找出重复的那个数字。如果你在运算中使用了辅助的存储方式,那么你能找到不用这种方式的算法吗?

仔细分析

看过我之前文章或者我视频教程的同学可以已近发现了除了排序的算法之外,还可以通过牺牲空间换取时间的方法来解决它。那现在我们就把可能的方法都先列出来,然后我们再比较一下优缺点。

1. 排序: 那么唯一重复的两个数肯定排列在了一起,我们再扫描这个数组,如果当前元素和上一个元素相等的话,这个元素应该就是我们要找的。那排出来的结果可能是: 1,2,3,4..., 888, 888, 889,... 1000

  • 优点:看起来很清晰,
  • 缺点:排序最好的复杂度能做到n logn(牺牲空间的方法先不讨论哈)

2. 计数

我们之前做过一个计数排序对吧,大概的思路是我们已知了数组元素的取值范围。这道题中,我们申明1000个空间的素组, 1对应下标0, 2对应下标1, 1000对应下标999, 当然1001个空间会更加方便。扫描原来的数组一遍,遇到一个元素我们在新的数组中对应下标位置中加1,最后必定有一个元素的次数是两次。所以统计好了只有,我们扫描计数数组也就自然得到了结构。

核心伪代码如下:

int[] count = new int[1001];

for(int index = 0; index

{

   count[input[index]]++;

}

for(int index = 0; index < count.length; index++)

{

    if(count[index] >1) return index;

}

  • 优点:线性复杂度
  • 缺点:有辅助存储空间,题目当中提到应该有更好的办法

3. 借助于数学方法

严格来说,很多计算机问题最后都是数学问题。大家设想下,1-1000里每个元素出现一次,乱序的,我们对它怎么求和呢? Sum = (1+1000)*1000/2, 等差数列对吧。现在这里多出了一个元素,那我们是否知道该如何求出这个数了呢? 这个target = Sum(element in array from 0 to 1000) - (1+1000) * 1000 / 2;对吧?

看来求和就可以了?但是大家在仔细想一想,求和可能会出现什么问题吗?可能会越界,我没有去论证它,但是大家想想,如果我们把1000再放大一点,这个越界问题终究会出现的对吧?我们需要改加法为减法。避免越界

  • 优点:用数学问题帮助解决,扫描一遍即可,没有辅存空间,O(n)的复杂度
  • 缺点:加法可能越界,需要改加法为减法操作

伪代码

我们现在就看着个被我们优化过的数学方法,怎么解决哈,我们已经假设输入的数组是满足条件的了哈,不做额外的检查了,大家看伪代码:

public int Find(int[] input)

{

    int target = 0;

    for(int index = 0; index < input.length; index++)

    {

        target = target + input[index] - index -1;

    }

   return target + input.length;

}


算法分析:

我们一起分析下上面的伪代码哈。 我们再对原数组求和的过程中,做了一点事情,每次加一个我们就减掉一个N。我们做这样一个假设,数组是排好序的(帮助大家理解), 里面有5个元素, 1, 2, 3,4,5,5。

当我们的index = 0时,target 开始等于0, target = 0 + 1 -(0) -1 = 0

当我们的index = 1时, target 还是等于 0 ,target = 0 + 2 -(1)-1 = 0,

当我们的index =2 时, target还是0, target = 0 + 3 - (2) - 1 = 0, 

当我们的index = 3时, target还是0, target = 0 +4 - (3)-1=0,

当我们的index = 4时, target还是0, target = 0+5-(4)-1 = 0,

当我们的index = 5时,target之前是0, 接下来 target  = 0 +5 -5-1 = -1

这个时候我们就会发现,我们要找的数 应该是 -1 +N = -1 +6 = 5.

大家可以在这个假设中,我们把顺序调整一下。这个规律是不是也满足,那我们把N放大一点,这个算法是不是也成立呢?

源代码

  public static int Find(int[] input)
        {
            int target = 0;

            for(int index = 0; index

测试方法

算法数据结构面试分享(十一)火眼金睛,从队伍里迅速找出那唯一的一个双胞胎_第1张图片


好了,欢迎大家关注我的公众号,还有我的系列视频教程, 数据结构与算法 微软经典算法面试题辅导。大家有什么更好的解法,也欢迎讨论哈。

算法数据结构面试分享(十一)火眼金睛,从队伍里迅速找出那唯一的一个双胞胎_第2张图片

你可能感兴趣的:(经典面试-算法数据结构)