【问题标题】:What is the bug in this code?这段代码中的错误是什么?
【发布时间】:2011-10-07 04:05:06
【问题描述】:

基于this 逻辑作为对 SO 对不同(相似)问题的回答,为了以 O(N) 时间复杂度删除数组中的重复数字,我在 C 中实现了该逻辑,如下所示。但是我的代码的结果没有返回唯一的数字。我尝试调试,但无法获得解决此问题的逻辑。

int remove_repeat(int *a, int n)
{
    int i, k;

    k = 0;
    for (i = 1; i < n; i++)
    {
        if (a[k] != a[i]) 
        {
            a[k+1] = a[i];
            k++;            
        }
    }
    return (k+1);
}

main()
{
    int a[] = {1, 4, 1, 2, 3, 3, 3, 1, 5};
    int n;
    int i;

    n = remove_repeat(a, 9);

    for (i = 0; i < n; i++)
            printf("a[%d] = %d\n", i, a[i]);


} 

1] 上述代码中删除重复项的错误之处。

2] 此问题的任何其他 O(N) 或 O(NlogN) 解决方案。它的逻辑?

【问题讨论】:

  • 您在尝试调试时学到了什么?
  • 你的意图不清楚。我很清楚它不起作用,请用您自己的话描述您要编写的代码。
  • @Cody - 这个逻辑试图构建一个从 0 到 j 的唯一数字的子数组。
  • @Drakosha - 上面有什么不清楚的地方?尝试实现一个逻辑以从数组中删除重复元素(在这种情况下为整数),并仅返回同一数组中的唯一元素,同时尝试以 O(N) 时间复杂度完成它。
  • @goldenmean - 我明白,问题是你的想法是什么,即你打算怎么做。

标签: c arrays duplicate-removal


【解决方案1】:

您将需要两个循环,一个遍历源,一个检查目标数组中的每个项目。

不会得到 O(N)。

[编辑] 您链接到的文章建议使用 sorted 输出数组,这意味着在输出数组中搜索重复项可以是二进制搜索......这是 O(LogN)。

【讨论】:

  • 我认为文章说“从元素 a[1] 到 a[N]。在每个阶段 i,a[i] 左侧的所有元素都包含一个排序的元素堆a[0] 到 a[j]”。这意味着对于 i 的每次迭代, a[] 中 i 左侧的 a 中的元素都是通过此逻辑获得的排序元素?我错过了什么吗?
【解决方案2】:
  1. 在 O(n log n) 时间内进行堆排序。
  2. 在 O(n) 时间内迭代,用标记值(例如 INT_MAX)替换重复元素。
  3. 在 O(n log n) 中再次进行堆排序以提取出重复元素。

仍以 O(n log n) 为界。

【讨论】:

    【解决方案3】:

    您的代码似乎要求对输入进行排序。在测试时使用未排序的输入,您的代码不会删除所有重复项(仅删除相邻的)。

    【讨论】:

      【解决方案4】:

      你的逻辑错了,所以代码也错了。在编码之前自己做你的逻辑。 我建议使用修改堆排序的 O(NlnN) 方式。 使用堆排序,我们从 a[i] 连接到 a[n],找到最小值并将其替换为 a[i],对吗? 所以现在是修改,如果最小值与 a[i-1] 相同,则交换最小值和 a[n],将数组项的数量减少 1。 它应该以 O(NlnN) 的方式解决问题。

      【讨论】:

        【解决方案5】:

        如果整数的数量预先知道并且小于您拥有的内存量,您可以获得 O(N) 解决方案:)。一次通过来确定您使用辅助存储所拥有的唯一整数,然后再通过一次以输出唯一值。

        下面的代码是用 Java 编写的,但希望你能明白。

        int[] removeRepeats(int[] a) {
            // Assume these are the integers between 0 and 1000
            Boolean[] v = new Boolean[1000]; // A lazy way of getting a tri-state var (false, true, null)
        
            for (int i=0;i<a.length;++i) {
               v[a[i]] = Boolean.TRUE;
            } 
        
            // v[i] = null => number not seen
            // v[i] = true => number seen
        
            int[] out = new int[a.length];
            int ptr = 0;
            for (int i=0;i<a.length;++i) {
                if (v[a[i]] != null && v[a[i]].equals(Boolean.TRUE)) {
                    out[ptr++] = a[i];
                    v[a[i]] = Boolean.FALSE;          
                }
            }
        
            // Out now doesn't contain duplicates, order is preserved and ptr represents how
            // many elements are set.
            return out;
        }
        

        【讨论】:

        • @Jeff - 如果我也有 -ve 数字,这将不起作用,不是吗。查找是否看到数字的第一步可能会失败?有什么想法吗?
        • @Drakosha 所有循环都在输入数组上,所以我认为 O(N)?还是我误会了? @GoldenMean,如果你有一个受限制的宇宙(例如 -50 到 50 之间的数字,那么你可以简单地移动值。这与使用哈希表存储数字计数的方法基本相同,只是使用完美哈希代替传统数组 + 桶或树的实现。
        • @Jeff Foster:我的意思是初始化 vO(number of integers),在 O(1) 中初始化数组有一个技巧,但它需要更多内存。
        • @Drakosha 好的,我同意这一点!谢谢
        【解决方案6】:

        您的代码仅检查数组中的一项是否与其前任相同。

        如果您的数组开始排序,那将起作用,因为特定数字的所有实例都是连续的。

        如果您的数组未排序开始,那将不起作用,因为特定数字的实例可能不连续,因此您必须查看所有前面的数字以确定是否一个还没见过。

        要在 O(N log N) 时间内完成这项工作,您可以对数组进行排序,然后使用您已经拥有的逻辑从排序后的数组中删除重复项。显然,这仅在您可以重新排列数字时才有用。

        如果您想保留原始顺序,您可以使用哈希表或位集之类的东西来跟踪一个数字是否已被看到,并且仅在/如果尚未看到每个数字时将其复制到输出被看到。为此,我们会更改您当前的:

        if (a[k] != a[i])
            a[k+1] = a[i];
        

        类似于:

        if (!hash_find(hash_table, a[i])) { 
            hash_insert(hash_table, a[i]);
            a[k+1] = a[i];
        }
        

        如果您的数字都在相当窄的范围内,或者您希望这些值很密集(即大多数值都存在),您可能希望使用位集而不是哈希表。这只是一个位数组,设置为 0 或 1 以指示是否已看到特定数字。

        另一方面,如果您比一般情况更关心复杂性的上限,则可以使用平衡的基于树的集合而不是哈希表。这通常会使用更多内存并运行更慢,但其预期复杂度和最坏情况复杂度基本相同 (O(N log N))。一个典型的哈希表在最坏的情况下会从恒定复杂度退化为线性复杂度,这会将你的整体复杂度从 O(N) 变为 O(N2)。

        【讨论】:

          【解决方案7】:

          您的代码仅适用于特定情况。显然,您正在检查相邻的值,但重复值可能出现在数组中的任何位置。因此,这是完全错误的。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-04-09
            相关资源
            最近更新 更多