Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 查找列表中多个可能重复的整数中的任意一个_Algorithm_Search_Duplicates - Fatal编程技术网

Algorithm 查找列表中多个可能重复的整数中的任意一个

Algorithm 查找列表中多个可能重复的整数中的任意一个,algorithm,search,duplicates,Algorithm,Search,Duplicates,给定一个n+1整数数组,每个整数的范围在1到n之间,找到一个重复的整数 我是在一次工作面试中被问到这个问题的。我的答案是:鸽子洞原则说必须重复。我尝试使用二进制搜索方法,所以我在Matlab中这样做,因为我知道: top = 0; bot = 0; for i=1:n+1 if P[i] > n/2 top = top+1; else bot = bot+1; end 因此,我认为其中一个,top或bot,必须再次大于PhP的n/2。在那个范围内重复 我认为这是

给定一个
n+1
整数数组,每个整数的范围在
1
n
之间,找到一个重复的整数

我是在一次工作面试中被问到这个问题的。我的答案是:鸽子洞原则说必须重复。我尝试使用二进制搜索方法,所以我在Matlab中这样做,因为我知道:

top = 0;
bot = 0;
for i=1:n+1
  if P[i] > n/2 
    top = top+1;
  else
    bot = bot+1;
end
因此,我认为其中一个,
top
bot
,必须再次大于PhP的
n/2
。在那个范围内重复


我认为这是一个很好的解决方案,但面试官暗示,一个人可以做得更好。请发布您知道的任何更好的解决方案。

如果您知道只有一个数字是重复的,您可以通过将所有数字相加并将数字之和从1减去n来找到它:

duplicate = sum P[i] - n(n+1)/2
如果不是,则可以遍历数组并将每个数字放入哈希表中。如果号码已经存在,则为重复号码。这也是O(n),假设哈希表操作是O(1)

或者事件更好-为了避免哈希表,可以使用大小为n的布尔数组:

int[] P = new int[] { 3, 2, 5, 1, 4, 2 };
bool[] Q = new bool[6];

foreach( var p in P ){
    if ( Q[p] ) {
        Console.WriteLine("Duplicate: " + p);
        break;
    }
    Q[p] = true;
}

我不确定你是如何定义“更好”的,但也许这符合条件。至少它不同于你的解决方案和链表问题的解决方案(双关语)

如果我们走一条路

n+1 --> array[n+1] --> array[array[n+1]] --> ...
那么对于某些
k!=l
,即当且仅当存在重复时。这个问题现在变成了一个常见的链表问题,可以按如下方式解决

在第一个节点上启动两个粒子。让第一个粒子以单位速度移动,让第二个粒子以两倍单位速度移动。然后如果有一个循环,第二个粒子将在第一个粒子后面循环,最终它们将是相同的。为什么?好吧,如果你认为粒子在一个圆上(一旦找到循环,它们就会在这个圆上),在每一个时间单位,第二个粒子会有一个定向的步骤靠近第一个。因此,它们最终必须碰撞。一旦他们这样做了,你就找到了一个循环。要找到重复的值,只需让一个粒子静止,而另一个粒子再次运行循环,即可获得循环的长度。然后在起点处再次启动两个粒子,让其中一个粒子向前移动循环的长度,然后将两个粒子以恒定的距离一起运行,直到它们在循环起点处再次相遇

由于一些评论员对我没有包括如何在链表中找到循环的所有细节感到愤怒,现在就在这里。没有人保证这不是bug(毕竟这是Matlab式的伪代码),但它至少应该解释这个想法

%% STEP 1: find a point in the cycle of the linked list using a slow and fast particle
slow = n+1;
fast = n+1;
for i=1 to n+1
    slow = array[slow];
    fast = array[array[fast]];
    if (slow == fast)
        break;
end

%% STEP 2: find the length of the cycle by holding one particle fixed
length = 1;
fast = array[fast]
while fast != slow
    fast = array[fast];
    length = length+1;
end

%% STEP 3: find the repeated element by maintaining constant distance between particles
slow = n+1;
fast = n+1;
for i=1 to length
    fast = array[fast];
end
while fast != slow
    fast = array[fast];
    slow = array[slow];
end

%% STEP 4: return the repeated entry
return slow;

这里我从
n+1
开始,因为
array[I]
介于1和n之间,所以两个粒子都不会被送回
n+1
。这最多使一个粒子通过阵列(尽管不是按顺序),并跟踪两个粒子(慢和快)和一个整数(长度)。因此,空间是O(1),时间是O(n)。

这个简单的解决方案怎么样:

从数组开始创建二进制搜索树。 当您插入BST时,只要已经存在一个重复的元素,那么将该元素存储在另一个重复元素数组中并继续循环。我们甚至不需要对数组进行排序来查找重复元素

这只是我的想法。我在一次采访中被问到了同样的问题,这是我的答案。

for(inti=n+1;I!=a[I];I=a[I]);
for(int i=n+1;i!=a[i];i=a[i]);

cout<<i;

cout这与@PengOne的答案类似,但我相信更简单

说明:

//pseudocode
//O(n) time, O(1) space
findrepeating(a):
    x = findrepeating(a, findmeet(a), a[a.length() -1])
    return x

findmeet(a):
    slow = fast = a[a.length() -1]
    while true:
        slow = a[slow-1]
        fast = a[a[fast-1]-1]
        if slow == fast:
            break
    meet = slow // or fast
    return meet

findrepeating(a, meet, start):
    m = meet
    s = start
    while m != s:
        m = a[m-1]
        s = a[s-1]
    return m // or s
这种方法将数组视为一个图,其中索引
i
处的值指向索引
a[i]-1
(因此值
1
指向索引
0
)。至少有1个重复编号,因此图形将是循环的。有
n+1
元素,最大值为
n
,因此最后一个节点
a[n+1]
永远不会是循环的一部分,而是进入循环。这一点很重要,因为最后一个节点是遍历的
开始节点。请注意,如果作为循环一部分的节点被用作带有
慢速
(1x)和
快速
(2x)指针的
开始节点
,则它们在同一节点处相遇,这是没有用的。让我们将汇聚节点称为
meet node
。如果
满足节点
k
跳离
循环节点
,则
开始节点
也将是
k
跳离
循环节点
。此逻辑与在循环链表中查找循环节点相同。数组最多遍历3次,因此
O(n)
时间和
O(1)
空间

算法:

//pseudocode
//O(n) time, O(1) space
findrepeating(a):
    x = findrepeating(a, findmeet(a), a[a.length() -1])
    return x

findmeet(a):
    slow = fast = a[a.length() -1]
    while true:
        slow = a[slow-1]
        fast = a[a[fast-1]-1]
        if slow == fast:
            break
    meet = slow // or fast
    return meet

findrepeating(a, meet, start):
    m = meet
    s = start
    while m != s:
        m = a[m-1]
        s = a[s-1]
    return m // or s
  • 从最后一个节点(
    a[n+1]
    )开始,使用
    slow
    (1x)和
    fast
    (2x)指针查找
    meet节点
  • 集合节点
    开始节点
    前进两个指针(1x),它们将汇聚到
    循环节点
    (重复的数字指向
    循环节点
    代码:

    //pseudocode
    //O(n) time, O(1) space
    findrepeating(a):
        x = findrepeating(a, findmeet(a), a[a.length() -1])
        return x
    
    findmeet(a):
        slow = fast = a[a.length() -1]
        while true:
            slow = a[slow-1]
            fast = a[a[fast-1]-1]
            if slow == fast:
                break
        meet = slow // or fast
        return meet
    
    findrepeating(a, meet, start):
        m = meet
        s = start
        while m != s:
            m = a[m-1]
            s = a[s-1]
        return m // or s
    
    我们用他的想法来解决这个问题

    我们所需要做的就是首先找到圆的开头,然后在圆中找到重复的一个

    以下是c++中的代码:

    int findDuplicate(vector<int>& nums) {
        int slow = nums[0];
        int fast = nums[nums[0]];
    
        while(slow != fast){
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        fast = 0;
        while(slow != fast){
            slow = nums[slow];
            fast = nums[fast];
        }
    
        return slow;
    }
    
    int findDuplicate(向量和nums){
    int slow=nums[0];
    int fast=nums[nums[0]];
    while(慢!=快){
    慢=nums[慢];
    fast=nums[nums[fast]];
    }
    fast=0;
    while(慢!=快){
    慢=nums[慢];
    fast=nums[fast];
    }
    返回缓慢;
    }
    
    但可能会有很多重复,因此我认为这些问题与我的问题不同。而且,排序非常慢。“比我的回答慢多了。”马特·鲍尔和其他人很接近