(LeetCode)包含重复的III

时间:2015-06-29 15:42:44

标签: java algorithm tree

  

给定一个整数数组,找出是否有两个不同的   数组中的索引i和j使得nums [i]之间的差异   和n [j]最多为t,i和j之间的差异最大   ķ。

您好!

但是老实说,我对这个问题感到很难过。在这个问题的(LeetCode)讨论论坛中提供的解决方案没有提供太多关于如何解决它的解释/思考过程。我完全理解解决问题的技术,而不是给我完整的实现代码。我觉得这是最好的学习方式。

因此,这里的线索是使用(Java)TreeSet来解决这个问题。我假设地板和天花板方法在这里很有用。

如果有人在那里可以给我一点线索/暗示来解决这个问题,我会很感激。伪代码也很受欢迎!我之前没有说过,我不需要完整的实施代码。只是一个起点很棒! :)

谢谢!

编辑:我同时也在努力解决这个问题!所以,如果我最终得到答案,我会在这里发布以供将来参考。 :)

3 个答案:

答案 0 :(得分:3)

这个问题已经很老了,但是OP还没有公布他/她的回答...... 我会尝试向那些偶然发现这个问题的人解释。

以下答案基于存储桶,时间复杂度为 O(n)

基本思想是使用宽度为k的滑动窗口。我们的注意力将在此窗口中受到约束,因此此窗口中i和j(索引)之间的差异不能大于k。 我们检查这个窗口中是否有两个数字,最多不同于t。如果有这样的数字,那么我们就完成了。否则,我们的窗口将向前移动一步,我们将再次检查。

现在真正的问题是我们如何检查窗口中是否有两个这样的数字 。当然我们可以使用残余力,它将是O(K ^ 2),那么整个算法将是O(n * K ^ 2)。如果K不大,我们可以接受。

然而,通过使用桶,我们可以做得更好!

每当我们在窗口中遇到一个数字时,我们将它除以(t + 1)。假设结果是B,我们然后将数字放入桶[B]。

如果t = 3,那么数字0,1,2,3将全部放入桶[0],数字4,5,6,7将被放入桶[1]和8,9,10, 11将被放入桶[2]等。我们保证一个桶中的所有数字都会有不大于t的差异。还有一件事:4 - 2 = 2< 3他们在不同的桶里。是的,一些差异小于t的数字可能被放入不同的桶中。 但是,在这种情况下,它们只能在相邻的存储桶中。

以下是一个例子:

nums = [1, 5, 17, 6, 8], t = 2, k = 5 

(我们现在不必担心k,因为它与nums的长度相同。)

由于t = 2,所以每当我们在列表中遇到一个数字时,我们将其除以t + 1(使用整数除法)并将其放入相应的桶中。

1 / 3 = 0   -->   We put 1 in bucket[0]

5 / 3 = 1   -->   We put 5 in bucket[1]

由于相邻存储桶[1]中的存储桶中可能存在满足要求的元素,因此我们需要检查。 bucket [0]的编号为1,但是5 - 1> 3还没有桶[2],所以我们继续。

17 / 3 = 5   -->   We put 17 in bucket[5]

没有桶[4]或桶[6],所以我们继续。

6 / 3 = 2   -->   We put 6 in bucket[2]

我们必须检查桶[1]中的数字:| 5 - 6 | = 1< 2所以有这样的数字,我们返回True。

如果我们继续将8放在桶[2]中,我们会发现其中已经有一个元素,即6个。由于一个桶中的所有元素的差异不大于t,我们就完成了。

因此,为了检查是否有两个差异小于t的数字,我们将我们遇到的每个数字都放在一个桶中。如果该存储桶中已有元素,那么我们就完成了。否则,我们检查相邻桶是否有满足要求的元素,如果没有,我们继续将数字放入桶中。

我们差不多完成了。现在我们需要考虑窗口宽度k。在将所有k个数字放入桶中后,如果我们没有找到两个这样的数字,我们需要将窗口向前移动一步。也就是说,删除最左边的号码及其对应的桶,并在其桶中添加新号码。如果已经采取了它的桶,那么我们就完成了。否则我们检查它的相邻桶,如果有的话。

下面是我的Python代码。

if t < 0 or not nums or k <= 0:
    return False
buckets = {}
width = t + 1

for n, i in enumerate(nums):
    buck = i // width
    if buck in buckets:
        return True
    else:
        buckets[buck] = i
        if buck - 1 in buckets and i - buckets[buck-1] <= t:
            return True
        if buck + 1 in buckets and buckets[buck+1] - i <= t:
            return True
        if n >= k:
            del buckets[nums[n-k] // width]
return False

答案 1 :(得分:1)

首先想到的实现只有两个for循环,一个嵌套。

在内部for循环中检查abs的逻辑(nums [i] -nums [j])&lt; = t和abs(i-j)&lt; = k。

外循环:i从0到n

内环:j从i + 1到n

答案 2 :(得分:0)

最优解O(n)可以按照@Jing Zhao的解释来完成。但是,在Java中,您必须注意其他事情,例如整数溢出。

这是我在Java中的解决方案:

public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
    if (nums == null || k < 0 || t < 0) return false;
    Map<Integer, Integer> buckets = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int bucket = (int) Math.floorDiv(nums[i], (long) t + 1);
        if (buckets.containsKey(bucket)) return true;
        else {
            buckets.put(bucket, nums[i]);
            // Cast to long as it overflows if 2147483647 - (-1) => -2147483648
            if (buckets.containsKey(bucket - 1) && nums[i] - (long) buckets.get(bucket - 1) <= t) return true;
            if (buckets.containsKey(bucket + 1) && buckets.get(bucket + 1) - (long) nums[i] <= t) return true;
            if (buckets.size() > k) {
                buckets.remove((int) Math.floorDiv(nums[i - k], (long) t + 1));
            }
        }
    }
    return false;
}

首先,您必须处理负面的信息。例如,如果nums [i] = -1且t = 2:

int bucket0 = -1/3 = 0 // Remember to divide by (t+1) 

这不行,因为它还会在存储桶“ 0”中放置另一个数字,例如nums [i] = 2,并且由于它们在同一个存储桶中,因此将返回true。但是-1和2之间的距离是3,而不是2!因此它应该返回false。解决此问题的正确方法是使用Math.floorDiv:

int bucket1 = Math.floorDiv(-1,3) = -1

这会将-1放入存储桶“ -1”中,并且应该可以正常工作。对于负数,存储桶会有所变化。这让我很难理解,因此我尝试更详细地解释它。

首先,我们除以(t + 1)而不仅仅是“ t”的原因如下: 假设t = 2,并且如果仅除以“ t”,则可以在存储桶“ 1”中:

2,3 // Because 2/2 and 3/2 give "1"

但是此存储桶仅占“ 1”而不是“ 2”的差,例如,还应包括4,因为4-2为2。现在,如果我们除以(t + 1):

3/3,4/3,5/3 // Because all those divisions yield "1"

有了这个,我们可以确定距离“ t”(在本例中为2)内的所有值都在同一存储桶中。

我们可以看到存储桶包含从精确除法开始的所有值,在本例中为3/3,下一个存储桶“ 2”将从6/3开始。但是在负数的情况下,它们将从精确除法中再增加一个数。这是因为存储桶0中包含0。

bucket with "1" => 3,4,5
bucket with "0" => 0,1,2
bucket with "-1" => -1,-2,-3
bucket with "-2" => -4,-5,-6

如果将Java中的常规int除法用于-1,我们将得到:

-1/3 = 0 // This would go to bucket "0"
-2/3 = 0 // This would go to bucket "0"

这是因为Java中的常规int除法只会截断,因此,如果您得到的值是0.xxxx,它将被截断为0,或者如果是-1.XXX,则将被截断为-1。 但是使用Math.floorDiv,除法将获得该值的实际下限,因此如果值为负-1.xxxx,则将得到-2,对于-1/3,则将为-1。

希望这可以帮助解决此问题的人。该解决方案非常聪明,您必须处理许多不同的情况。