检测循环链表的此功能的时间复杂度是多少?

时间:2017-09-03 08:12:59

标签: c++ algorithm time-complexity

我只是尝试使用unordered_map来检测循环链表(我知道这是一个坏主意)。

我的类名为linked_list的isCyclic()函数的代码片段是:

    bool linked_list::isCyclic(Node * head)
{
  Node * trav = head;
  unordered_map<Node *, int > visited;

  while(trav != NULL)
  {
    if(visited[trav->next_ptr] == 3)
        return true;
    visited[trav] = 3;
    trav = trav->next_ptr;
  }
  return false;
}

对于每个新节点,我检查next_ptr是否指向已经访问过的节点,如果是,则返回true,否则我只是将该节点设置为已访问并将trav更新到下一个要访问的节点,直到出现循环情况或者直到遍历所有节点。

我不想知道这个算法的大O符号是什么,因为根据我的计算,我猜错了O(n * n),因为我总是不那么准确。

1 个答案:

答案 0 :(得分:0)

提供的算法的复杂性为O(n)。原因很简单,因为unordered_map和unordered_set都可以根据标准库的要求提供恒定的插入,搜索和删除时间。因此,给定平均常数时间k,复杂度变为O(k*n),相当于k*O(n),并且最终具有O(n)的基本复杂度作为常量{{} 1}}变得无关紧要。

为了证明这一点,以下示例将您的周期搜索范围缩小为使用k的最基本情况,该情况仅为std::unordered_set,其中键映射到self。

std::unordered_map

请注意,bool check_for_cycle_short(const Node *p) { std::unordered_set<const Node*> mm; for (; p && mm.insert(p).second; p = p->next); return p != nullptr; } 会返回std::unordered_set<T>::insert std::pair,其中后者表示插入是否实际发生,因为在插入之前未找到密钥。

现在考虑这个例子,即iterator,bool

O(n^2)

通过每个节点详尽地搜索列表的其余部分,从而执行bool check_for_cycle_long(const Node *p) { for (; p; p = p->next) { for (const Node *q = p->next; q; q = q->next) { if (q == p) return true; } } return false; } 比较。

示例

要查看这些内容,请考虑以下简短程序,该程序加载一个包含100000个节点的链表,然后在最坏情况下检查两个循环(没有):

(n-1) + (n-2) + (n-3).... + (n-(n-1))

输出(MacBook Air双核i7 @ 2.2gHz)

#include <iostream>
#include <iomanip>
#include <chrono>
#include <unordered_set>

struct Node
{
    Node *next;
};

bool check_for_cycle_short(const Node *p)
{
    std::unordered_set<const Node*> mm;
    for (; p && mm.insert(p).second; p = p->next);
    return p != nullptr;
}

bool check_for_cycle_long(const Node *p)
{
    for (; p; p = p->next)
    {
        for (const Node *q = p->next; q; q = q->next)
        {
            if (q == p)
                return true;
        }
    }
    return false;
}

int main()
{
    using namespace std::chrono;

    Node *p = nullptr, **pp = &p;
    for (int i=0; i<100000; ++i)
    {
        *pp = new Node();
        pp = &(*pp)->next;
    }
    *pp = nullptr;

    auto tp0 = steady_clock::now();
    std::cout << std::boolalpha << check_for_cycle_short(p) << '\n';
    auto tp1 = steady_clock::now();
    std::cout << std::boolalpha << check_for_cycle_long(p) << '\n';
    auto tp2 = steady_clock::now();

    std::cout << "check_for_cycle_short : " <<
        duration_cast<milliseconds>(tp1-tp0).count() << "ms\n";
    std::cout << "check_for_cycle_long  : " <<
        duration_cast<milliseconds>(tp2-tp1).count() << "ms\n";
}

正如所料,结果反映了我们的怀疑。