【问题标题】:Finding a circle in a linked list with lookup table使用查找表在链表中查找圆
【发布时间】:2012-10-22 16:39:28
【问题描述】:

我试图在链表中找到一个圆圈,并返回圆圈开头的节点。例如,如果列表是 A -> B -> C -> D -> C -- 我们将返回 C

这是我的代码:

ListNode<T> * findCircle()
{
  map<ListNode<T>*, bool> addresses;

  ListNode<T>* node = head;

  if(addresses.find(node)->second)
    cout << " wtf " << endl;

  while(node)
  { 
    if((addresses.find(node))->second)
    {
      return node;
    } 
    else
      addresses.insert(pair<ListNode<T>*,bool>(node, 1));

    node = node->next;
  }

  return NULL;

}

我有几个问题:

1) 这是解决问题的正确方法吗

2) 我是否使用最有效的方法来查找/插入键和值到表中

3) 为什么它不起作用?当我在地图中检查 head 时,在我插入 head 之前,它仍然执行 if 语句并打印“wtf”。我的算法是如果在地图中没有找到该节点作为具有真值的键插入,否则如果该键已经在地图中,则返回该节点。

我尝试使用 std::set 执行此操作,但它给我带来了麻烦,所以我切换到 map。令我困惑的是,以下代码使用完全相同的方法工作(使用查找表删除链表中重复项的代码)。

  void removeDuplicates()
    {
      map<T, bool> listData;

      ListNode<T> *node;
      ListNode<T> *prev = node;
      for(node = head; node; node = node->next)
      {
        if(listData.find(node->data)->second)
        {
          prev->next = node->next;
          delete node;
        }
        else
        {
          listData.insert( pair<T, bool>(node->data, 1));
        }
        prev = node;
      }
    }

第二个代码块完成了它应该做的事情,而第一个代码块却没有,这只是侥幸吗?

【问题讨论】:

  • 您可能应该考虑使用类似“龟兔赛跑”的算法,请参阅wikipedia
  • 啊,是的,弗洛伊德 Warshall 算法。忘记了。为什么我的查找表想法很愚蠢?只是出于学习目的而想知道
  • 对于“wtf”,find 返回一个指向它找到的元素的迭代器,或者如果它没有找到任何元素,则返回“one-past-the-end”迭代器。您正在取消引用此迭代器,因此您的代码具有未定义的行为。
  • 正确。但这带来了更多问题。具体来说,当我说 find(node) != map::end --- 编译器抱怨我使用的 std::map 没有模板参数。另外,为什么我包含的第二个功能可以完美地工作,使用几乎完全相同的技术。这只是运气吗?
  • "我尝试使用 std::set 执行此操作,但它给我带来了麻烦,所以我切换到 map。" - 为什么不问一个关于set 问题的问题,并准确记录问题所在? - 没有理由使用map。顺便说一句-“删除重复项”功能已完全损坏并且具有未定义的行为-取消对尚未看到的节点的end() 的引用-即使在最好的情况下,我也无法想象它“有效”。

标签: c++ map linked-list hashmap


【解决方案1】:
addresses.find(node)->second

find() 失败时,当前会产生未定义的行为,因为您正在尝试访问过去迭代器的second 字段。相反,使用

addresses.find(node) != addresses.end()

另外:找到一个循环的确切位置是否重要(这就是它通常所说的),或者你只是想看看一个循环是否存在?如果是后者,那么一个非常聪明的算法可以仅使用两个指针在线性时间和恒定空间中解决问题,其中一个指针的移动速度是另一个指针的两倍:)

【讨论】:

  • 啊啊啊谢谢!! address.end() 是我需要的。至于你的问题,我需要返回循环开始的节点。但是书上的解决方案是两指针的解决方案,所以在那种情况下它必须工作
  • 龟兔算法一般不会找到循环开始的节点——它只保证会找到a节点如果有一个循环。
  • 我不明白它如何知道是否有循环。当一个指针加一,另一个加二时,什么表示存在循环?
  • @ordinary:我找到的关于龟兔算法的最佳解释是 templatetypdef,这里:stackoverflow.com/a/5130334/47984。希望有帮助!
【解决方案2】:

你需要检查find操作是否真的找到了数据

auto iterator itr = address.find(node);
if(itr != address.end()){
    if(itr->second == true){ cout << "WTF" << endl; }
}

类似于 while 循环。至于您的方法,我认为它具有 O(n) 的最佳运行时间。你所能做的就是降低常数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    相关资源
    最近更新 更多