了解将弗洛伊德的乌龟和野兔算法应用于整数数组后为何起作用

问题描述

我正尝试使用自己的乌龟和野兔算法实现解决{leetcode}问题https://leetcode.com/problems/find-the-duplicate-number/,当给定以下整数数组时,该算法将导致无限循环:

[3,1,3,4,2]

仅在跟踪算法后,我才能看到慢跑者和快跑者永远不会同时使用两个重复值。这是我的伪代码算法:

initialize fast and slow runners to 0

while(true)

   move fast runner two indices forward
   move slow runner one index forward

   if arr[fast] == arr[slow] and fast != slow
      return arr[fast] // this is the duplicate

现在,我敢肯定,精通离散数学的人将能够直观地知道,这种方法不会导致正确的解决方案,而不必先像我必须做的那样追溯一个例子。

我可以做出哪些推断或观察,从而使我看到该算法无法正常工作?我想知道如何通过一系列逻辑语句直观地识别此逻辑中的缺陷。换句话说,为什么在这个例子中两个跑步者永远找不到重复的东西,这是什么解释?我觉得这可能与计数有关,但是我在离散方面没有很强的背景。

为了澄清,我已经查看了正确的实现,因此我确实知道解决该问题的正确方法。我只是以为这种方式与将其应用于链接列表的效果非常相似,在该列表中,您会将快速流道向上移动两个节点,将慢速流道向上移动一个节点。谢谢您的帮助。

解决方法

当您在链表中检测到循环时,弗洛伊德(Floyd)的乌龟算法会起作用。它依赖于以下事实:如果两个指针以不同的速度移动,则它们之间的间隙将继续增加到一个极限,此后如果存在周期,它将被重置。
在这种情况下,该算法确实找到了一个循环,因为在某些迭代之后,两个指针都收敛到索引0。但是,您不想在这里检测到一个周期。您正在尝试查找重复项。这就是为什么它陷入无限递归的原因:它旨在检测一个循环(正确地执行了该循环),而不是检测其基本实现中的重复项。

为澄清起见,这是在示例数组上创建的示例链接列表。

3 -> 1 -> 3 -> 4 -> 2
'--<----<----<----<-'

如果运行弗洛伊德(Floyd)算法,则会发现该循环将在索引0处被检测到,因为两个指针都将在此处收敛。通过检查fastslow是否指向相同位置,而不是检查它们是否具有相同的节点值(fast==slowfast.value==slow.value

您正在尝试通过比较节点上的值并检查节点是否未指向同一位置来检查重复项。实际上这就是缺陷,因为弗洛伊德(Floyd)的算法可以检查两个指针​​是否都指向同一位置以检测周期。
您可以阅读this simple,informative proof来提高您的直觉,以了解为什么指针会收敛。

,

这不是一个坏主意。这是Python的实现:

class Solution:
    def findDuplicate(self,nums):
        slow,fast = 0,0
        while True:
            slow = nums[nums[slow]]
            fast = nums[fast]
            if slow == fast:
                break

        fast = 0
        while True:
            slow,fast = nums[slow],nums[fast]
            if slow == fast:
                break
        return slow

我们还可以使用二进制搜索:

class Solution:
    def findDuplicate(self,nums):
        lo,hi = 0,len(nums) - 1
        mid = lo + (hi - lo) // 2
        while hi - lo > 1:
            count = 0
            for num in nums:
                if mid < num <= hi:
                    count += 1
            if count > hi - mid:
                lo = mid
            else:
                hi = mid
            mid = lo + (hi - lo) // 2
        return hi

在C ++中:

// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    return 0;
}();


// Most of headers are already included;
// Can be removed;
#include <iostream>
#include <cstdint>
#include <vector>


static const struct Solution {
    static const int findDuplicate(
        const std::vector<int>& nums
    ) {
        int slow = 0;
        int fast = 0;

        while (true) {
            slow = nums[nums[slow]];
            fast = nums[fast];

            if (slow == fast) {
                break;
            }
        }

        fast = 0;

        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }

        return slow;
    }
};

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...