嵌套了if语句和while循环

时间:2021-03-10 19:24:09

标签: c++ time-complexity

void makeGraph(){
    for(auto itr = inputs.begin(); itr != inputs.end(); itr++){
        string str = itr->second;
        if (strstr(str.c_str(), "->") != NULL){
            char ch = '>';
            size_t pos = str.find_last_of(ch);
            parent = str.substr(0, pos-2);
            string child = str.substr(pos+2);
            if (strstr(parent.c_str(), ",") != NULL){
                int size_parent = parent.length();
                char ch_parent[size_parent+1];
                strcpy(ch_parent, parent.c_str());
                char *token = strtok(ch_parent, ",");
                while (token != NULL){
                    string sub_Parent = token;
                    sub_Parent.erase(std::remove(sub_Parent.begin(),
                                                 sub_Parent.end(),
                                                 ' '),
                                     sub_Parent.end());
                    graph[sub_Parent].push_back(child);
                    token = strtok(NULL, ",");
                }
            } else{
                graph[parent].push_back(child);
            }
        } else{
            cout<<"Just for TEST"<<endl;
        }
    }
    printGraph();
}

这个函数的时间复杂度是多少?是 n O(n) 吗?怎么处理else语句里面的循环?

1 个答案:

答案 0 :(得分:3)

这种情况下的困难在于您需要考虑许多变量。重要的是:

  1. input 中的元素数量。
  2. 对于 input 中的每个字符串,字符串中的字符数。

如果您想要一个精确的估计,您必须分别考虑这些变量中的每一个。如果我们使用 N 来表示 input 的长度,那么我们有 N 个字符串,其长度为 M1, M2, ..., {{ 1}}。每个字符串都可以有不同的处理方式,这也让事情变得复杂。

由于 Big-O 提供了一个上限,我们可以通过考虑这些变量来为自己省去一些麻烦:

  1. MN,是 N 中元素的数量。
  2. input,是最长输入字符串的长度。

最后一个大的简化来自最坏情况下的行为。在不了解输入数据的情况下,我们无论如何也无法真正进行更细微的计算,所以我们只做最坏的情况。

因为这里发生了很多事情,所以我喜欢看“叶子”——嵌套最多的块——然后按照我的方式工作。让我们来看看房间里的大象:M 循环。这里发生了什么?循环本身基本上只是迭代 while。请注意,parent 的长度可以与 parent 的长度成线性比例,这意味着 str 循环的复杂度将类似于 while。正文只由几个步骤组成:

O(M * [complexity of the while body])

涉及 string sub_Parent = token; sub_Parent.erase(std::remove(sub_Parent.begin(), sub_Parent.end(), ' '), sub_Parent.end()); graph[sub_Parent].push_back(child); token = strtok(NULL, ","); sub_Parent 的工作影响了 token 循环的循环行为(即,while 循环迭代次数越少,{{1 }} 通常是,反之亦然)。但是,这些操作中的任何一个都不会比 while 更糟糕,因为 token 的长度本身就是 O(M)。相反,让我们专注于 parent 调用。由于O(M)的长度是push_back(),并且由于这个child复制了O(M),所以这个操作的时间复杂度是push_back()。因此,我们可以自信地说,while 循环体的复杂度也是 child。将主体与循环本身结合起来,while 循环的总复杂度为:

O(M)

现在让我们上一层。 O(M) 循环之外的操作显然是 O(M * [complexity of the while body] = O(M * M) = O(M^2) ,因为它们处理复制和搜索 whileO(M) 循环在此占主导地位,因此我们可以忽略这些贡献。

再上一层,我们击中了 parent。我们在这里做什么?那么,在 while 的情况下,我们得到复杂度为 iftrue 循环。在 while 的情况下,我们得到一个带有 O(M^2) 副本的 false,使 push_back()。由于我们正在考虑最坏的情况,因此我们会说 child/O(M) 的复杂性是更大的表达式:if

再上一层(就在上一步的 else 之外),我们遇到了与以前类似的情况,即操作最糟糕的是 O(M^2),因为它们搜索和复制 {{1} } 创建 ifO(M)。这些主要由 str 复杂性决定,因此我们可以忽略它们的贡献。

现在是最外面的 parent。我们再次比较分支。在 child 分支中,我们有刚刚计算出的 if 复杂度。在 if 分支中,我们有 true 复杂性(只是打印一个常量字符串)。因此,在最坏的情况下,此 O(M^2) 具有 false 复杂性。

我们快到了。在那个 O(1) 之外,我们有一个副本来创建 if。这是 O(M^2),因此我们可以忽略它,因为它由 if 中的 str 支配。所以 O(M) 循环体总共是 O(M^2)

最后我们看看 if 循环本身。它是对 for(长度为 O(M^2) 个元素)的简单线性迭代,这意味着其复杂度为:

for

所以你有它。最坏情况的复杂度在 input 的长度上是线性的,但在最长字符串的长度上是二次的。