我最近遇到了这个有趣的问题:
“考虑每个节点的链接列表,除了让'next'指针也有一个'随机'指针。'随机'指针指向链表上的一些随机的其他节点。它也可能指向NULL。为简化起见,没有两个“随机”指针指向同一个节点,但多于1个Node的随机指针可以指向NULL。
现在我们需要反转Linked列表的所有指针('next'和'random')的方向。约束是解决方案必须是O(1)空间复杂度(可以创建恒定数量的新节点但不与列表的长度成比例)“
我花了很多时间考虑这个。我真的不相信它实际上是可能的。
答案 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;
}