降低两个for循环的时间复杂度,并将其优化为1 for循环

时间:2018-11-06 12:47:53

标签: c++ algorithm

我想优化此循环。它的时间复杂度为 n 2 。我想要类似 n 或log( n )的东西。

for (int i = 1; i <= n; i++) {
        for (int j = i+1; j <= n; j++) {
            if (a[i] != a[j] && a[a[i]] == a[a[j]]) {
                x = 1;
                break;
            }
        }
    }

a[i]满足1 <= a[i] <= n

3 个答案:

答案 0 :(得分:1)

这就是我会尝试的:

让我们用a [](即集合{a [i]})将图像称为B:B = {b [k]; k = 1..K,因此我存在,a [i] = b [k]}

对于每个b [k]值,k = 1..K,确定集合Ck = {i; a [i] = b [k]}。
B和Ck的确定可以在线性时间内完成。

然后让我们逐一检查集合Ck。
如果Card(Ck} = 1:k ++
如果Card(Ck)> 1:如果Ck的两个元素是B的元素,则x = 1;其他k ++

我将使用表(std::vector<bool>)来存储1..N元素是否属于B。

我希望不要犯错。暂时没有时间编写程序。稍后我可以做,但我想您将能够轻松完成。

注意:发送此答案后,我发现@Mike Borkland在评论中已经提出了类似的建议...

答案 1 :(得分:1)

由于有时您需要查看学习的解决方案,因此我为您提供了一个小功能,可以完成您想要的工作。希望对您有所帮助。

#define MIN 1
#define MAX 100000 // 10^5

int seek (int *arr, int arr_size)
{   

    if(arr_size > MAX || arr_size < MIN || MIN < 1)
            return 0;

    unsigned char seen[arr_size];
    unsigned char indices[arr_size];

    memset(seen, 0, arr_size);
    memset(indices, 0, arr_size);

    for(int i = 0; i < arr_size; i++)
    {   
            if (arr[i] <= MAX && arr[i] >= MIN && !indices[arr[i]] && seen[arr[arr[i]]])
                    return 1;
            else
            {   
                    seen[arr[arr[i]]] = 1;
                    indices[arr[i]] = 1;
            }   
    }   

    return 0;
}   

好吧,这如何以及为什么起作用?首先,让我们看一下原始算法试图解决的问题;他们说,解决方案的一半是有充分陈述的问题。问题在于查找在给定的整数数组 A 中大小为 n 的元素是否绑定在一个和 n ([1, n ])在 A 中存在两个元素,即 x y ,这样 x ! = y Ax = Ay (位于索引 x y 的数组, 分别)。此外,我们正在寻找一种具有时间复杂度的算法,以便对于 n = 10000,该实现在1秒钟内运行。

首先,让我们开始分析问题。在最坏的情况下,需要至少一次对阵列进行完全扫描,以确定阵列中是否存在此类元素对。因此,我们不能比 O n )做得更好。但是,你会怎么做呢?一种可能的方法是扫描数组并记录是否出现给定的索引,这可以在另一个数组 B (大小为 n )中完成;同样,记录是否出现了与被扫描元素的索引处的 A 相对应的给定数字,这也可以在另一个数组 C 中完成。如果在扫描时数组的当前元素没有显示为索引,而是显示为元素,则返回yes。我不得不说,这是使用类似哈希表的数据结构的“经典技巧”。

原始任务是:i)降低时间复杂度(来自 O n ^ 2)),以及ii)确保实现在一个对于大小为10000的数组,则为1秒。建议的算法以 O n )的时间和空间复杂度运行。我用随机数组进行了测试,看来该实现的工作要比要求的快得多。

编辑:我的原始答案不是很有用,谢谢指出。检查完注释后,我认为代码可以有所帮助。

编辑2:我还添加了有关其工作原理的说明,因此可能很有用。希望对您有所帮助:)

答案 2 :(得分:-1)

  

我想优化此循环。它的时间复杂度为n 2 。我想要类似 n log(n)的东西。

最简单的方法是首先对数组进行排序。这是 O(n log(n)),然后查找两个相邻元素的线性扫描也是 O(n),因此在时,主导复杂度不变> O(n log(n))

您知道如何使用std::sort,对吗?而且您知道复杂度是 O(n log(n))吗?

您可以弄清楚如何调用std::adjacent_find,并且可以看到复杂度必须是线性的?


最佳的复杂度是线性时间。这仅允许我们对数组进行恒定数量的线性遍历。这意味着,如果我们需要进行查找以确定每个元素,那么我们是否之前就已经看到过该值-它必须是恒定时间。

您知道具有恒定时间插入和查找的任何数据结构吗?如果是这样,您可以编写一个简单的单遍循环吗?

提示:std::unordered_set是固定时间成员资格测试的通用解决方案,Damien对std::vector<bool>的建议对于您的特定情况可能更有效。