我必须在链表中交换两个相邻节点(而不是它们的数据)。 例如 1)输入a-> b-> c-> d-> e-> f,输出:b-> a-> d-> c-> f-> e 2)输入a-> b-> c-> d-> e,输出:b-> a-> d-> c-> e
我写了下面的代码是否有更有效的方法(可能有两个临时指针)或简单的逻辑?
node* swap(node* head) {
node *first = head;
node *second,*third,*result;
if(head == NULL || head->next == NULL) {
return head;
}
result = second = first->next;
third = second->next;
while(second != NULL) {
second->next=first;
first->next=(third->next==NULL ? third : third->next);
first=third;
second=(third->next==NULL ? third : third->next);
third=(second==NULL ? second : second->next);
}
return result;
}
答案 0 :(得分:3)
你可以通过递归相当简单地做到这一点:
// Swaps node b and c.
void swapTwo(node* a, node* b, node* c) {
if (a != NULL)
a->next = c;
b->next = c->next;
c->next = b;
}
void swapEveryTwo(node* prev, node* node) {
if (node != null && node->next != null) {
swapTwo(prev, node, node->next);
swapEveryTwo(node->next, node->next->next);
}
}
swapEveryTwo
的每次调用都会交换节点对,然后为下一对设置递归。此外,由于此函数是尾递归的,因此编译器无疑会将其优化为while
循环,从而确保不会分配额外的堆栈帧,因此将是最佳的。如果您需要进一步解释,请随时提出。
已修改为原始帖子中添加swap
功能:
node* swap(node *head) {
if (head != NULL && head->next != NULL) {
node *newHead = head->next;
swapEveryTwo(NULL, head);
return newHead;
} else {
return head;
}
}
答案 1 :(得分:2)
您的算法是最好的。通常我们可以通过简单性获得一点速度。考虑从输入列表的头部弹出元素并使用队列add-to-tail操作来构建结果,而不是绘制关于指针的图片和推理。在伪代码中,我们有
set_empty(rtn);
while (head) {
fst = pop(head);
if (head) {
snd = pop(head);
add_at_tail(rtn, snd);
}
add_at_tail(rtn, fst);
}
仅需要if
来防止输入列表具有奇数长度的情况。如果我们确定列表的长度是均匀的,我们可以跳过它。
现在pop非常容易实现。如果我们使用虚拟头节点,则add-to-tail操作最简单。所以在C中,我们有:
node *swap(node *head)
{
node dummy[1]; // set_empty(rtn);
node *rtn = dummy;
while (head) {
node *fst = head; // fst = pop(head);
head = head->next;
if (head) {
node *snd = head; // snd = pop(head);
head = head->next;
rtn->next = snd; // add_to_tail(rtn, snd);
rtn = rtn->next;
}
rtn->next = fst; // add_to_tail(rtn, fst);
rtn = rtn->next;
}
rtn->next = NULL; // terminate tail
return dummy->next;
}
现在我还没有测试过这段代码,但我很确定它会运行很好的模数,也许是一两个错字。测试比你的少(每个元素只有一个)。测试相对昂贵,因为它们可能会干扰流水线操作,所以我的应该运行得更快一点。几乎可以肯定,这种差异是无关紧要的。
但是,我认为我的代码更容易理解。当然,这只是一种偏见,但在维护期间可读性确实很重要。
NB 现在我做了一个快速测试,它第一次尝试就开始了!另一方面,当我尝试你的代码时,我得到了一个segv
first->next=(third->next==NULL ? third : third->next);
以下是测试框架。你看错了吗?
typedef struct node_s {
struct node_s *next;
int val;
} node;
// swap goes here
int main(void)
{
node dummy[1];
node *p = dummy;
for (int i = 0; i < 16; i++) {
p->next = malloc(sizeof(node));
p = p->next;
p->next = NULL;
p->val = 'a' + i;
}
p = swap(dummy->next);
while (p) {
printf("%c ", p->val);
p = p->next;
}
printf("\n");
return 0;
}
答案 2 :(得分:1)
看起来不错。我添加了一个正确性检查(第三个== NULL)并删除了一个冗余表达式。您只需浏览整个列表一次,您必须这样做。所以我认为我们可以肯定这是最快的方法。
node* swap(node* head) {
node *first = head;
node *second,*third,*result;
if(head == NULL || head->next == NULL) {
return head;
}
result = second = first->next;
third = second->next;
while(second != NULL) {
second->next=first;
second = first->next=((third==NULL || third->next==NULL) ? third : third->next);
first=third;
third=(second==NULL ? second : second->next);
}
return result;
}
答案 3 :(得分:1)
在JavaScript中
LinkedList.prototype.swapPairs = function() {
var recurse = function(current) {
if (!current) return this;
if (current.next) {
var save = current.next.value;
current.next.value = current.value;
current.value = save;
current = current.next;
}
return recurse(current.next);
}.bind(this);
return recurse(this.head);
}
答案 4 :(得分:0)
Alex DiCarlo的递归方法更简单,但需要稍微纠正。
void swapEveryTwo(node* prev, node* node) {
if (node != null && node->next != null) {
swapTwo(prev, node, node->next);
swapEveryTwo(node, node->next);
}
}
如果我错了,请纠正我。
谢谢!