使用具有随机指针的节点反转链接列表

时间:2011-12-07 05:12:37

标签: linked-list

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

“考虑每个节点的链接列表,除了让'next'指针也有一个'随机'指针。'随机'指针指向链表上的一些随机的其他节点。它也可能指向NULL。为简化起见,没有两个“随机”指针指向同一个节点,但多于1个Node的随机指针可以指向NULL。

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

我花了很多时间考虑这个。我真的不相信它实际上是可能的。

4 个答案:

答案 0 :(得分:3)

很有可能。我提出了一个可能不是最佳的解决方案,但表明它可以完成。首先将它分解为两个问题:反转下一个指针并反转随机指针。

扭转下一个指针:

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随机指针)。我们需要几个辅助函数来完成它。第一个是反转随机列表的一个。我们主要复制上面的代码。请注意,我们将链的末尾设置为特殊值。这使我们无法重新反转列表。请参阅评论中的讨论以获得解释。

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)附近运行。这很可能不是最快的解决方案。它确实使用了恒定的空间(尽管可以通过内嵌和积极的变量共享来减少它。)

答案 1 :(得分:3)

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

答案 2 :(得分:2)

(扩展康斯坦丁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

答案 3 :(得分:0)

以下是我对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;
}