问题陈述如下:
给定一个单链表,写一个函数来成对交换元素。例如,如果链表是1-> 2-> 3-> 4-> 5-> 6-> 7那么该函数应该将其改为2-> 1-> 4- > 3-> 6-> 5-> 7,并且如果链表是1-> 2-> 3-> 4-> 5-> 6那么该函数应该将其改为2→1→4-> 3→6-→5
这是我的解决方案:
void swapPairs(Node* head) {
if (!head->next || !head)return head;
Node* current = head->next;
Node*prev = head;
while (true) {
Node* next = current->next;
current->next = prev;
if (!next || !next->next) {
prev->next = next;
break;
}
prev->next = next->next;
prev = next;
current = prev->next;
}
}
解决方案的问题是,当它到达函数的末尾时,磁头不再指向列表的开头,因为当前和prev都被移动了。我在网上看到过其他解决方案与我在这里做同样的事情,但也许因为它们是用C而不是C ++实现的,所以它们的代码有一些方面允许头部保持指向开头的清单。
我意识到这可能是一个非常简单的问题需要解决,但我目前还没有看到解决方案。 在整个算法中维护列表开头的最佳方法是什么?
答案 0 :(得分:2)
一旦你进行轻微的范式转换,手头的任务就会简单得多。这可能看起来更复杂,但在稍微思考之后,它的简单性应该是显而易见的。
简单地说:当你遍历列表时,不是携带指针到每对列表元素的第一个,而是携带一个指针指向每对列表元素中的第一个。从表面上看,这听起来很复杂;但是一旦尘埃落定,这就大大简化了手头的任务。
您必须单独存储头指针,并将其传递给swapPairs()
:
Node *head;
// The list gets populated here, then ...
swapPairs(head);
这就是你现在正在做的事情。但是,不是这样做,而是将指针传递给头节点,而不是:
swapPairs(&head);
// ...
void swapPairs(Node **ptr)
{
while ( (*ptr) && (*ptr)->next)
{
// swap the next two elements in the list
// ...
// Now, advance by two elements, after swapping them.
ptr= &(*ptr)->next->next;
}
}
while循环的主体将交换列表中的下两个元素,让我们暂时跳过该部分,并专注于迭代此链表的逻辑位,一次一对元素。这里发生了什么?
嗯,你正试图一次走两个元素。
请记住,ptr
不再是指向该对的第一个元素的指针。它指向指向该对的第一个元素的指针恰好存在的指针。因此,最初的ptr
指向您原来的head
指针。
一旦你明白了,只要列表中至少剩下两个元素,下一个心理跳跃就是要明白你想要循环迭代:
while ( (*ptr) && (*ptr)->next)
*ptr
是列表的下一个元素,该对中的第一个元素,而(* ptr) - > next因此是指向该对中第二个的指针。由于我们如何重复,ptr
可以通过合同证明永远不会NULL
。因此,如果*ptr
为NULL,则到达列表的末尾。但是如果*ptr
不是NULL,则至少剩下一个元素,而(* ptr) - > next是" next 2nd element"。如果(*ptr)->next
为NULL,则原始列表中包含奇数个元素,因此在最后一次迭代中,您最终使用*ptr
指向奇数鸭。在这种情况下你也完成了,不需要再进一步了。
最后:
ptr= &(*ptr)->next->next;
这只会使列表中的两个元素前进ptr
。请记住,ptr
是一个指向指向列表中接下来两个元素中第一个元素的指针的地方",现在将ptr
设置为指向的位置指向***的第一个*** ****列表中的两个元素恰好存在。拿一张纸,绘制一些图表,然后自己弄清楚。不要过去"去",不要收200美元,直到它沉入。
一旦你将大脑包裹起来,唯一剩下的就是交换对子。就是这样。循环的主体可以简单地是:
Node *first=*ptr;
Node *second=first->next;
Node *next=second->next;
*ptr=second;
second->next=first;
first->next=next;
那就是它。你完成了。你猜怎么着?您的原始head
现在自动指向交换列表的右侧头节点。
现在,那不容易吗?
答案 1 :(得分:1)
将功能的签名更改为
Node* swapPairs(Node* head)
并在第一次交换后保存新头,然后在while
循环关闭}
后结束,返回新头,以替换调用者持有的旧列表头,这将使用这样的东西:
list = swapPairs(list);
答案 2 :(得分:1)
您可以在函数开头保留指向新头的指针,并使用该函数更新函数末尾的head
。请注意,我将参数从Node*
更改为Node **
,因为我们要更新指针值,以便在更新head
时,我们写入传递给此函数的相同内存位置。因此,当我们从函数返回时,head函数的调用者可以获得正确的head值。
void swapPairs(Node **head) {
if (!(*head)->next || !(*head))
return;
Node *new_head = (*head)->next; // the new_head
Node *current = (*head)->next;
Node *prev = (*head);
while (true) {
Node *next = current->next;
current->next = prev;
if (!next || !next->next) {
prev->next = next;
break;
}
prev->next = next->next;
prev = next;
current = prev->next;
}
*head = new_head; // now update the head
}