【问题标题】:Reversing a linkedList with Nodes having a random pointer使用具有随机指针的节点反转linkedList
【发布时间】:2012-01-14 16:37:29
【问题描述】:

我最近遇到了一个有趣的问题:

"考虑每个节点的链表,除了有一个'next'指针之外还有一个'random'指针。'random'指针指向链表上的某个随机其他节点。它也可能指向NULL。为了简单起见,没有两个“随机”指针会指向同一个节点,但是超过 1 个节点的随机指针可以指向 NULL。

现在我们需要反转链表的所有指针('next' 和 'random')的方向。约束是解决方案必须是 O(1) 空间复杂度(可以创建恒定数量的新节点,但与列表的长度不成比例)"

我花了很多时间思考这个问题。我不太相信这实际上是可能的。

【问题讨论】:

    标签: linked-list


    【解决方案1】:

    您还需要考虑随机链形成(简单)循环的情况。 您可以通过链的线性遍历来检测循环;如果循环中有偶数个节点,则必须再次处理重新反转。

    【讨论】:

      【解决方案2】:

      很有可能。我想出了一个可能不是最佳的解决方案,但表明它可以完成。先把它拆成两个问题:反转next指针和反转随机指针。

      反转下一个指针:

      node* last = NULL;
      node* current = head;
      node* next = head->next;
      while (current != NULL)
      {
        current->next = last;
        last = current;
        current = next;
        if (current != NULL)
          next = current->next;
      }
      head = last
      

      反转随机列表有点棘手,因为我们没有随机指针链的所有头的列表,但我们可以找到它们的末端(节点将是一个 NULL 随机指针)。我们将需要几个辅助函数来完成它。第一个是反转随机列表。我们主要从上面复制代码。请注意,我们将链的末端设置为特殊值。这会阻止我们重新反转列表。有关解释,请参阅 cmets 中的讨论。

      node* chainTail = malloc(1); //mallocing here to get a unique pointer
      void reverseRandom(node* rhead)
      {
        node* last = chainTail;
        node* current = rhead;
        node* next = rhead->random;
        while (current != NULL)
        {
          current->random = last;
          last = current;
          current = next;
          if (current != NULL)
            next = current->random;
        }
      }
      

      我们还需要一个辅助函数来查找节点的父节点(如果没有,则返回 NULL)。我们将做一个愚蠢的线性搜索:

      node* findParent(node* target)
      {
        node* candidate = head;
        while ((candidate != NULL) && (candidate->random != target))
          candidate = candidate->next;
        return candidate;
      }
      

      现在我们只需遍历列表,找到随机值为 NULL 的任何节点(我们的链尾),找到它们的链头,然后反转链:

      node* current = head;  //Current  node in a linear walk looking for chain tails
      while (current != NULL)
      {
        if (NULL == current->random)
        {
          //current is the tail of a random chain, lets find the head
          node* curr = current; //Current node in the search for the chain hean
          node* parent = findParent(curr);
          while (parent != NULL)
          {
            curr = parent;
            parent = findParent(curr);
          }
          //At this point, curr is the head of the random chain, so reverse it
          reverseRandom(curr);
        }
        current = current->next;
      }
      
      //Clean up chainTail pointers
      node* current;
      for (current = head; current != NULL; current = current->next)
      {
        if (current->random == chainTail)
        {
          current->random = NULL;
        }
      }
      free(chainTail); //Stop a memory leak if this is not a global
      

      标准免责声明:我没有运行此代码。它可能有错误。快结束的时候我开始昏昏欲睡,所以我可能犯了一个逻辑错误,但在我看来它是有效的。

      此外,如果您希望将其投入生产,请不要这样做。这段代码在 O(n^3) 附近运行。这很可能不是最快的解决方案。它确实使用了常量空间(尽管可以通过内联和积极的变量共享来减少)。

      【讨论】:

      • 感谢您的回答。但是,我只有一个问题。你如何避免重新反转随机链?假设您已经反转了一个随机链,您在主列表开头的某个地方偶然发现了该链。稍后,当您遍历主列表时,您遇到了一个节点,该节点是您已经反转的早期随机链的成员.. 要做到这一点,您需要额外的簿记.. 如果没有额外的存储空间,您如何跟踪它?
      • @arun_suresh 你是对的,有双重反转的风险,但不是你预期的。下一个指针反转很好,因为您只反转第一个函数中的下一个指针。您在之后反转随机指针并保留下一个指针,因此不存在交叉污染的风险。此外,由于没有两个节点指向同一个随机指针,这也是安全的。唯一的风险是链的头部在链表中比尾部更远,因此我们在碰到尾部时首先反转它,然后在碰到新尾部时再次反转它。
      • 好的,是的,简单的解决方案:让新链的尾端不是 NULL,而是具有特殊值,然后在我们完成反转后清理它们。我将编辑问题。
      【解决方案3】:

      (扩展 Konstantin N. 的回答)

      为确保您不会重新反转任何内容,您可以逐个遍历列表,一次一个节点,从左到右,并存储最后检查的节点的索引。

      lastIndex <- 0
      cur <- head
      
      while cur.next not null:
          if cur.random is null:
              randomHead <- # use Konstantin N's findParent helper
              if get_index(randomHead) < lastIndex:
                  # we've already examined it
              else:
                  # Konstantin N's reverseRandom()
          cur <- cur.next
          lastIndex <- lastIndex + 1
      
      // helper to find node index
      get_index(node):
          cur <- head
          ret <- 0
      
          while cur.next not null:
              if cur == node:
                  ret <- ret + 1
          return ret
      

      【讨论】:

      • 我认为这不会解决我的代码存在的问题(请参阅我的评论)。想象一下,我们有一个 1>2>3>4 的随机链。它应该被反转为 4>3>2>1,但是当我们到达节点 4 时,lastIndex 变量被设置为 3 而 randomHead 为 2,所以我们将忽略它。
      【解决方案4】:

      以下是我对时间复杂度为 O(n) 的 O(1) 解空间解的尝试。

      static Node<E> reverseList(Node<E> head) {
        if(head == null || head.next == null) {
          return head;
        }
      
        Node<Integer> current = head;
        Node temp = null;
      
        // Reverse an existing Linked list.
        while(current != null) {
          Node previousNext = current.next;
          current.next = temp;
          temp = current;
          current = previousNext;
        }
      
        reversedHead = temp;
        while(temp != null) {
          if(temp.jump != null) { 
            Node jumpTemp = temp.jump; 
            jumpTemp.jump = temp;  
            temp.jump = null; 
          }
          temp = temp.next;
        }
      
        return reversedHead;
      }
      

      【讨论】:

        猜你喜欢
        • 2011-07-27
        • 2014-02-13
        • 1970-01-01
        • 2015-02-26
        • 1970-01-01
        • 2021-02-19
        • 1970-01-01
        • 2017-01-04
        • 1970-01-01
        相关资源
        最近更新 更多