【问题标题】:CodeFight firstDuplicate interview challengeCodeFight firstDuplicate 面试挑战
【发布时间】:2017-08-12 21:22:20
【问题描述】:

根据问题陈述:

写一个时间复杂度为 O(n) 且额外空间为 O(1) 的解 复杂。给定一个数组 a 只包含范围内的数字 从 1 到 a.length,找到第一个重复的数字 第二次出现的索引最小。换句话说,如果有 超过 1 个重复的数字,返回第二个的数字 出现的索引小于另一个的第二次出现 号码可以。如果没有这样的元素,返回 -1

我根据约束遵循了我的代码,但仍然遇到时间复杂度错误。这是我的解决方案:

int firstDuplicate(std::vector<int> a)
{
    long long int n = a.size();
    int cnt=0;
    for(long long int i=0;i<n;i++)
    {
        //cout<<a[i]<<"      "<<cnt<<endl;
        if(a[i]==n||a[i]==-n)
        {    cnt++;
            if(cnt>1)
                return n;
        }
         else if(a[abs(a[i])]<0)
            return -a[i];
        else
            a[a[i]] = -a[a[i]];
    }
    return -1;
}

谁能建议我更好的算法或者这个算法有什么问题?

【问题讨论】:

  • 如果你需要帮助调试你的程序,你必须说它有什么问题。这可能涉及找到它没有完成预期或太慢的测试用例。某些网站产生“时间复杂度错误”是不够的。
  • @PaulHankin 代码在 10^5 范围内的 n 值非常大时失败。我不能在这里提供测试用例。毫无疑问,上面的代码在给定的测试用例上成功运行(范围为 10^3 - 10^4。
  • 也许在小测试用例上运行代码并检查越界数组访问。

标签: algorithm


【解决方案1】:

这个问题的算法如下:

对于数组中的每个数字a,每次看到该数字时,我们都会将a[abs(a[i]) - 1] 设为负数。在遍历a 时,如果在某个时刻我们发现a[abs(a[i] - 1] 是负数,我们返回a[i]。如果我们到达数组中的最后一项但没有找到负数,我们返回-1

我觉得,这就是你想要达到的目的,但你可能把事情复杂化了。在代码中是:

int firstDuplicate(std::vector<int> a)
{
    for (int i = 0; i < a.size(); i += 1)
    {
        if (a[abs(a[i]) - 1] < 0)
            return abs(a[i]);
        else
            a[abs(a[i]) - 1] = -a[abs(a[i]) - 1];
    }

    return -1;
}

这在 O(n) 时间内运行,空间复杂度为 O(1)。

【讨论】:

  • 谢谢它有效!但是我的一个和你的对我来说似乎相同,除了我使用从 1 到 n-1 的数组和额外的计数器变量来跟踪第 n 个变量。你能指出我的错误吗?
【解决方案2】:

你可以使用索引来标记一个元素之前是否出现过,如果idx处的值为负数,那么它之前已经出现过

int firstDuplicate(std::vector<int> a)
{
    long long int n = a.size();
    int cnt=0;
    for(long long int i=0;i<n;i++)
    {
        int idx = a[i] - 1;
        if(a[idx] < 0){
            return a[i];
        }
        a[idx] *= -1;
    }
    return -1;
}

【讨论】: