6

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

这是我到目前为止写的:

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;
4

8 回答 8

11

假设我们有:

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

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

因此,要交换 Node2和Node3 ,您实际上必须交换Node1->next和。这将起作用,即使它们彼此相邻(或者即使它是同一个节点)。例如:Node2->nextNode2->nextNode3->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

换了!

正如评论部分中的 unwind 所指出的,如果将 Node1 与任何东西交换,您必须为链表设置一个新的头。


针对问题的编辑:

您的交换代码几乎是正确的。但是,您需要将 firstPrev 与 secPrev 交换。在我的示例中恰好发生了next两次交换节点的值之一,因为它们彼此相邻。但从逻辑上讲,我们要交换next前两个的 s,然后交换next实际节点的 s。试试这个:

//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 变量 - 这可能是某处的分配或删除错误。你从哪里得到段错误?

于 2009-10-08T06:48:38.927 回答
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;
}

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

于 2009-10-08T06:58:01.913 回答
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; 
}
于 2014-09-22T11:41:20.407 回答
1

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

于 2011-10-06T15:08:56.750 回答
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;   
    }
于 2014-01-28T22:36:06.750 回答
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);
}
于 2009-10-08T07:41:10.903 回答
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;
 }

} }

于 2013-02-28T10:57:34.067 回答
0

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

首先,无论我多久查看一次 - 在白板或调试器中 - 如果我不使用条件来区分情况,我似乎无法避免在第一个节点中出现循环,即指向自身节点是否相邻,即使有抽象步骤或 Smashery 当前接受的答案中的具体代码。我在互联网上环顾四周,以找到当前接受的答案风格的代码,以避免这种条件,但对我来说,避免这种情况非常棘手,而且我的搜索没有找到这样的解决方案(除非我错了关于提议的一个可能不正确)。如果有一些巧妙的表达式可以在它们相邻时产生第一个节点的地址,而在它们不相邻时产生第一个节点的后继地址,

其次,对于在接受的答案中对相同变量的连续分配,我与其他评论员有同样的问题。我希望我在这里不是非常密集,而是按顺序为同一个变量分配不同的值,除非可能有副作用,对我来说,似乎永远不会给变量留下除最后一次赋值之外的任何其他值,不管我考虑的节点配置是什么,因此之前的分配显然是多余的。如果我对此有误,并且该方法实际上可以解决此问题,那么我将能够消除下面代码中的最后一个条件,当我第一次在互联网上寻找解决方案时,我试图摆脱它在没有特殊情况的相邻节点的情况下交换节点。我不太确定,

第三,在这个网站和其他网站上,我经常看到其他答案的声明重复,最好交换节点的内容,而不是指针。当然,在简单整数的情况下,就像到目前为止的示例一样,这显然会产生更短、更简单的代码。但是,当我们讨论带有包含整数的节点的链表时,它通常是作为更复杂和通用容器的支持数据结构的替身。因此,我不认为交换节点的内容真的那么容易,至少如果数据结构的实现不能对容器项的复制语义做出假设的话。此外,能够像这样交换节点的内容意味着链表对这些节点的内容拥有所有权,

不过,我承认这可能取决于容器的语义。对于一个数组,交换方法可能会改变引用下面的值,使其指向该数组的某个索引。这意味着引用并不意味着指向特定对象,而是指向容器中可以索引的位置。如果我们将链表视为仅对一组对象进行排序的一种方式,这些对象在链表之外使用,用户可能会期望交换操作仅交换位置,而不是内容。

例如,假设链表表示“汽车”类型的对象。每辆车都有一个车主,车主通过指向它的指针来引用他们的车。现在假设链表表示一组汽车计划在汽车经销商处进行维修以供检查的顺序。如果我们交换两个节点的内容,以便交换两辆汽车的时间表插槽,并通过交换它们的内容来做到这一点,那么服务实际上会以新的、正确的顺序发生——但人们也会突然拥有不一样的车!(不过,我不介意换特斯拉,因为我只开卡罗拉。)

如果链表(如数组示例)基于索引语义,则节点中的位置可能仅表示将汽车装载到船上进行运输的顺序。在这一点上,汽车没有任何所有者,我们真的只关心它们在哪个插槽中。然后,我想换掉汽车真的没有什么坏处,即节点引用的对象的内容。

最后到代码。正如我上面所说,我无法避免相邻节点的特殊情况。

一、定义一个辅助方法:

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;
}

此方法通过其值查找节点。返回的整数是链表中节点的从零开始的位置(如果愿意,可以将其称为索引)。我发现通过位置而不是指针比较来检测邻接更具可读性。列表的开头是根。该方法将 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 的节点值的位置、节点和前辈。如果要交换的第二个节点出现在第一个节点之前,则第一个和第二个节点的值将全部交换。这样做的代码并不多,减少了特殊的大小写,并使其更容易可视化。

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

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

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

于 2014-11-17T22:52:15.447 回答