在单个链表上交换节点

时间:2009-10-08 06:42:36

标签: c++ linked-list swap

我正在尝试创建一个swapNode函数,可以接受任意两个节点并交换它们。我已经制定了一个算法,如果它们至少有两个节点,那么它可以工作,但我似乎无法想出一个算法,如果它们彼此更接近就会有效。

这是我到目前为止所写的内容:

void swapNode(call * &head, call * &first, call * &second){
    call * firstPrev = NULL;
    call * secPrev = NULL;
    call * current = head;

    //set previous for first
    while((current->next != first) ){
        current = current->next;
    }

    firstPrev = current;
    current = head;

    //set previous for second
    while((current->next != second) ){
        current = current->next;
    }

    secPrev = current;
    current = second->next;

    //set firstPrev-> next to second
    firstPrev->next = second;
    //set secPrev->next to first
    secPrev->next = first;
    //set second->next = first->next
    second->next = first->next;
    //set first->next to current
    first->next = current;

    current = head;
    while(current->next != NULL){
        cout << current->number << endl;
        current = current->next;
    }

    cout << current->number << endl;
}

编辑: 我现在将此作为我的交换部分,但它似乎仍然无法正常工作

//swap firstPrev-> next with second->next
tmp = firstPrev->next;
second->next = firstPrev->next;
second->next = tmp;
//swap swap first->next with second->next
tmp = first->next;
second->next = first->next;
second->next = tmp;

EDIT2: 这个似乎也不起作用,我得到一个段错误。

    //swap previous's->next
    tmp =firstPrev->next;
    secPrev->next = firstPrev->next;
    secPrev->next = tmp;
    //swap swap first->next with second->next
    tmp = first->next;
    second->next = first->next;
second->next = tmp;

9 个答案:

答案 0 :(得分:11)

说我们有:

Node1 -> Node2 -> Node3 -> Node4 -> Node5

要交换两个节点,您需要在每个节点之前交换next个值,以及要交换的节点的next值。

因此,要交换节点2和节点3,您实际上必须将Node1->nextNode2->next交换,将Node2->next交换为Node3->next。这将起作用,即使它们彼此相邻(或者即使它是相同的节点)。例如:

交换Node1->nextNode2->next

Node1->next = Node3
Node2->next = Node2

使用Node2->next

交换Node3->next
Node2->next = Node4
Node3->next = Node2

这就是:

Node1 -> Node3 -> Node2 -> Node4 -> Node5

已交换!

在评论部分注意到,如果将Node1与任何内容进行交换,则必须为链表设置一个新头。


回答问题的修改:

用于交换几乎的代码。但是,您需要将firstPrev与secPrev交换。我的例子就是这样,我们将节点的next值之一交换两次,因为它们彼此相邻。但从逻辑上讲,我们想要交换前两个的next,然后交换实际节点的next。试试这个:

//swap firstPrev-> next with secPrev->next
tmp = firstPrev->next;
secPrev->next = firstPrev->next;
secPrev->next = tmp;
//swap swap first->next with second->next
tmp = first->next;
second->next = first->next;
second->next = tmp;

如果您遇到段错误,请检查tmp变量 - 这可能是某处分配或删除的错误。你在哪里得到段错?

答案 1 :(得分:7)

在大多数现实场景中,交换值将是最佳解决方案:

void swapNode(call * &head, call * &first, call * &second) {
    // swap values, assuming the payload is an int:
    int tempValue = first->value;
    first->value = second->value;
    second->value = tempValue;
}

如果不允许这样做,那么您希望在 - &gt; next而不是 - &gt; value组件上进行类似样式的交换。然后在firstPrev-&gt; next和secondPrev-&gt; next组件上进行另一次交换。注意第一个或第二个==头部的特殊情况。

答案 2 :(得分:2)

您还必须交换前一节点的next组件,否则链接列表将不会保持连接在一起。请注意,我的结构称为node

int swapNode( node *&head * &first, node * &second)
{
     //first we will declare the 
     //previous of the swapping nodes
     node *firstprev=NULL;
     node*secprev=NULL;
     node*current=head;
     //set previous first
     while(current->next!=first)
     {
        current=current->next;
     }
     firstprev=current;
     //seting 2nd previous
     while(current->next!=second)
     {
        current=current->next;
     }

    // swap values, assuming the payload is an int:
    int tempValue = first->value;
    first->value = second->value;
    second->value = tempValue;
    //swaping next of the nodes
    firstprev->next=second;
    secprev->next=first;
    return; 
}

答案 3 :(得分:1)

经验法则:“始终将数据与指针分开,永远不要交换指针,只交换数据!”。不使用memcpy()使交换显式,因此可以避免对齐问题。它在算法复杂性方面不会造成性能损失,但会使您的代码更具可读性和安全性。

答案 4 :(得分:1)

这里p1是要交换的第一个节点,p2是要交换的第二个节点。而prevnode是p2之前的节点

        temp=head;
        while(temp!=NULL){
        if(temp->link==p1){
           temp->link=p2;

           prevnode->link=p2->link; 
           p2->link=p1->link;
           t=p1->link;
           while(t!=prevnode)
              t=t->link;
              cout<<" YES";
           cout<<"["<<t->num<<"] ";
           p1->link=prevnode->link;
           prevnode->link=p1;   

           temp=p1;
        }//if_

        cout<<" "<<temp->num;              
        temp=temp->link;   
    }

答案 5 :(得分:0)

虽然我不是100%确定答案应该涉及对节点指针(或指针指针)的引用,然后这应该处理其中一个节点也是列表头部的情况。

void swapNodes(node *&first, node *&second)
{
  node *t = second->next;
  second->next = first->next;
  first->next = t;
  t = second;
  second = first;
  first = t;
}

然后你可以打电话给它,例如:

swapNodes(head, secPrev->next);

swapNodes(firstPrev->next, head);

swapNodes(firstPrev->next, secPrev->next)

它应该自动运行。

编辑:

swapNodes可能更具可读性:

void swapNodes(node *&first, node *&second)
{
  std::swap(first->next, second->next);
  std::swap(first, second);
}

答案 6 :(得分:0)

如果您想知道在简单C中交换链接列表中的两个节点的所有方法,请访问此链接:

http://sahilalipuria.wordpress.com/2013/01/21/swapping-two-nodes-of-a-singly-linked-lists-set-2/

答案 7 :(得分:0)

void swap()
{
 struct node *temp=0,*nxt,*ptr;
 ptr=head;
 int count=0;
 while(ptr)
 {
   nxt=ptr->link;
   if(nxt)
 {
  if(count==0)
    head=nxt;
    count++;
   ptr->link=nxt->link;
   nxt->link=ptr;
   if(temp!=NULL)
   temp->link=nxt;
   temp=ptr;
   if(ptr->link==NULL)
   break;
   ptr=nxt->link->link;
 }

} }

答案 8 :(得分:0)

谢谢大家的答案!我确实已经意识到这个问题差不多在两年前被提出并且答案早已被接受,但我对答案感到有些困惑。因此,尽管提问者可能不关心新答案,但我想添加我的版本,以防其他读者也感到困惑并记录我自己的方法。其中一些可能更适合作为评论,但我还没有评论的声誉。

首先,无论我多久看一次 - 在白板上或在调试器中 - 我似乎无法避免在第一个节点中以循环结束,即指向自身,如果我不使用条件区分节点相邻与否的情况,即使是抽象步骤或Smashery当前接受的答案中的具体代码。我已经在互联网上查了一下,以当前接受的答案的方式找到代码,以避免这样的条件,但对我来说,避免和我的搜索没有出现这样的解决方案是惊人的棘手(除非我错了关于提议的可能是不正确的)。如果存在一些聪明的表达式,当它们相邻时会产生第一个节点的地址,而当它们不存在时会产生第一个节点的后继地址,那么就不需要那个条件,因为计算第二个节点的新继任者是(显然)必须有条件的。

其次,我对接受的答案中与其他评论员连续分配相同变量的问题相同。我希望我在这里不是非常密集,但是按顺序为同一个变量分配不同的值,除非在副作用的情况下,对我来说,似乎永远不会将变量留给除最后一个赋值之外的任何其他值,无论如何我考虑的节点配置,从而使以前的分配显然是多余的。如果我错了,这种方法实际上解决了这个问题,那么我就能够消除下面代码中的最后一个条件,当我第一次在互联网上寻找解决方案时,我试图摆脱它交换节点没有特殊套管相邻节点。我不太确定,但听起来像Smashery故意将这些重复的任务从逻辑严谨中删除并更好地说明程序 - 但我可能误解了。

第三,在这个和其他网站上,我经常看到其他答案的陈述重复说,最好交换节点的内容,而不是指针。当然,在简单整数的情况下,如在到目前为止的示例中,这显然产生更短,更简单的代码。但是,当我们讨论包含整数的节点的链表时,它通常作为更复杂和通用容器的支持数据结构的替代。因此,我不认为交换节点的内容真的很容易,至少如果数据结构的实现不能假设容器的项目的复制语义。此外,能够交换节点的内容,这意味着链接列表拥有这些节点的内容的所有权,因为否则链接列表的方法之外的代码可能保留对那些节点中的对象的引用节点,其值突然在它们下面发生变化。

但是,我承认,这可能取决于容器的语义。对于数组,可能需要交换方法来更改对该数组的某个索引的引用下的值。这意味着引用不是指特定对象,而是指容器中可以索引的位置。如果我们将链表视为仅排序一组对象的方法,这些对象在链表之外使用,则用户可能希望交换操作仅交换位置而不是内容。

想象一下,例如,链表表示&#34; car&#34;类型的对象。每辆车都有一个车主,车主通过指针引用他们的车。现在假设链表表示一组汽车被安排在汽车经销商处进行检查的订单。如果我们交换两个节点的内容,为了交换两辆车的计划槽,并通过交换他们的内容来做,那么服务实际上将以新的正确的顺序发生 - 但人们也最终会突然拥有不同的车! (但我不会介意与特斯拉交换,因为我只驾驶卡罗拉。)

如果链接列表(如数组示例中所示)基于索引语义,那么节点中的位置可能只是表示将汽车装载到船上进行传输的顺序。在这一点上,汽车没有任何车主,我们真的只关心他们所在的位置。然后,我认为交换汽车真的不会受到伤害,即所引用的对象的内容节点。

最后到代码。正如我上面所说,我还没有能够避免相邻节点的特殊外壳。

首先,辅助方法的定义:

int find_node(int v, node* root, node** n, node** pn) {
    int i = 0;
    for (*n = root; *n != NULL && (*n)->v != v; ++i) {
        *pn = *n;
        *n = (*n)->next;
    }
    return i;
}

此方法按其值查找节点。返回的整数是链表中节点的从零开始的位置(如果您愿意,可将其称为索引)。我发现通过位置检测邻接,而不是指针比较,更具可读性。列表的开头是root。该方法将n设置为指向包含传递值的节点。在pn中,该方法存储n。

的前驱

以下是实际交换:

void swap_nodes(node **root, int v1, int v2) {
    if (v1 == v2) return;

    node *n1, *n2, *pn1, *pn2;

    int p1 = find_node(v1, *root, &n1, &pn1);
    int p2 = find_node(v2, *root, &n2, &pn2);

    if (p1 > p2) {
        std::swap(n1, n2);
        std::swap(pn1, pn2);
        std::swap(p1, p2);
    }

    if (p1 == 0) *root = n2; 
    else pn1->next = n2;

    node* nn1 = n1->next;
    n1->next = n2->next;

    if (p2 - p1 > 1) {
        n2->next = nn1;
        pn2->next = n1;
    } else {
        n2->next = n1;
    }
}

抱歉,我已经改变了OP方法的签名。我发现将节点的值传递给交换更方便,而不是节点指针。如果仅将节点指针传递到相应的节点,则必须进行另一次遍历才能找到具有此解决方案的前置任务,这对我来说感觉有点尴尬。如果我们不能通过这些值区分节点,例如值不是唯一的,但我们需要指向节点的指针。

与上面对find_node的解释一样,我们首先找到通过v1和v2传递给swap_nodes的节点值的位置,节点和前驱。如果要交换的第二个节点出现在第一个节点之前,则第一个和第二个节点的值都将被交换。这样做的代码不多,减少了特殊的外壳,使其更容易可视化。

现在,我们只剩下两个条件,这两个条件似乎都无法避免。如果第一节点位于链表的头部,即位置0,则根需要指向第二节点。否则,第一个节点的前任将指向第二个节点。

在节点不相邻的情况下,需要记住第一个节点的后继者的先前值。然后,第一节点的后继者被设置为第二节点的当前后继者。这是适用于所有情况的唯一更改:第一个节点的新后继者是第二个节点的旧后继者是唯一的确定性并且有助于开始,以便在实现此交换时记住指针操作及其顺序

最后,如果节点的位置相差多于一个,则它们不相邻。然后,第二个节点的新后继者成为第一个节点的旧后继者 - 保存在上面 - 并且第二个节点的前任现在指向第一个节点。如果它们相邻,则要交换的节点之间没有需要更新的节点,因此只需将第二个节点链接到第一个节点就可以了。