C++ C++;:不正确地交换链表中的节点

C++ C++;:不正确地交换链表中的节点,c++,pointers,C++,Pointers,我有两个简单的结构: struct Address { char city[255]; }; typedef Address* AddressPtr; struct Person { char fullName[255]; Address* address; Person* next; }; typedef Person* PersonPtr; Person结构形成一个链表,其中新元素添加到列表的开头。我想做的是按fullName对它们进行排序。起初我试图交换

我有两个简单的结构:

struct Address
{
    char city[255];
};
typedef Address* AddressPtr;

struct Person
{
    char fullName[255];
    Address* address;
    Person* next;
};
typedef Person* PersonPtr;
Person结构形成一个链表,其中新元素添加到列表的开头。我想做的是按
fullName
对它们进行排序。起初我试图交换链接,但我丢失了列表的开头,结果我的列表被部分排序。然后我决定通过交换节点的值对列表进行排序。但我得到了奇怪的结果。对于名称为:
Test3、Test2、Test1
的列表,我得到了
Test3、Test3、Test3

这是我的分类代码:

void sortByName(PersonPtr& head)
{
    TaskPtr currentNode, nextNode;
    for(currentNode = head; currentNode->next != NULL; currentNode = currentNode->next)
    {
        for(nextNode = currentNode->next; nextNode != NULL; nextNode = nextNode->next)
        {
            if(strcmp(currentNode->fullName, nextNode->fullName) > 0)
            {
                swapNodes(currentNode, nextNode);
            }
        }

    }
}

void swapNodes(PersonPtr& node1, PersonPtr& node2)
{
    PersonPtr temp_node = node2;
    strcpy(node2->fullName, node1->fullName);
    strcpy(node1->fullName, temp_node->fullName);

    strcpy(node2->address->city, node1->address->city);
    strcpy(node1->address->city, temp_node->address->city);
}
排序完成后,节点值有点奇怪

已更新

以下是我交换链接的方式:

void swapNodes(PersonPtr& node1, PersonPtr& node2)
{
    PersonPtr temp_person;
    AddressPtr temp_address;

    temp_person = node2;
    node2 = node1;
    node1 = temp_person;

    temp_address = node2->address;
    node2->address = node1->address;
    node1->address = temp_address;
}

问题产生于这样一个事实,即在遍历列表时交换列表的元素


另一种方法是在列表中运行并交换对象,直到它在列表中运行一次,而不交换任何元素。

为了交换单链接列表中的节点,您需要的不仅仅是要交换的两个节点。至少有三个指针需要修改

+--------+ next +---------+ next +---------+ next +---------+
| before |----->|  node1  |----->|  node2  |----->|  after  |
+--------+      +---------+      +---------+      +---------+
要交换
node1
node2
,您需要交换
node1->next
node2->next
,然后指向
before->next
。当然,为了修改
before->next
,您需要知道
before
。(如果节点不相邻,则还必须更新
node2
的父节点。)

现在,如果只是交换数据而不是指针,那么只交换两个节点就可以了。但如果这是目标的话,你有两件事做错了

  • temp_节点
    =
    node2
    。这意味着它们都指向同一个物体。因此,当您将数据从
    *node1
    复制到
    *node2
    中时,您正在修改相同的对象
    临时节点
    指向。然后,当您从
    *temp_node
    复制到
    *node1
    时,实际上是在复制刚刚覆盖的数据。最终结果:所有节点的地址对象都包含相同的位

    您需要
    temp_node
    指向一个独立的对象,或者干脆让它成为一个节点对象而不是指针,或者将数据复制到缓冲区或其他地方

  • 如果要完成某项工作,交换指针和交换数据是互斥的。在这里,交换节点指针,然后交换数据指针。。。?这有点违背目的,不是吗

    观察:

        //    node1             node2
        //    +----------+ next +----------+
        //    |   node   |----->|   node   |
        //    +----------+      +----------+
        //          | address        | address
        //          v                v
        //    +----------+      +----------+
        //    | address1 |      | address2 |
        //    +----------+      +----------+
    
        temp_person = node2;
        node2 = node1;
        node1 = temp_person;
    
        //    node2             node1
        //    +----------+ next +----------+
        //    |   node   |----->|   node   |
        //    +----------+      +----------+
        //          | address        | address
        //          v                v
        //    +----------+      +----------+
        //    | address1 |      | address2 |
        //    +----------+      +----------+
    
    到目前为止,一切顺利。两个指针交换。 这意味着
    node1->address
    可以看到第二个节点的地址

    但是等等:然后交换地址指针

        temp_address = node2->address;
        node2->address = node1->address;
        node1->address = temp_address;
    
        //    node2             node1
        //    +----------+ next +----------+
        //    |   node   |----->|   node   |
        //    +----------+--+   +----------+
        //           address|        | address
        //          +-------|--------+
        //          v       |
        //    +----------+  |   +----------+
        //    | address1 |  +-->| address2 |
        //    +----------+      +----------+
    
    现在,
    node1->address
    指向
    address1
    。您已取消对数据的屏蔽

    因此,决定是否要交换指针或数据。如果交换数据,则只需要两个节点的指针,而不修改
    next
    指针。另一方面,如果要交换整个节点,则需要“before”节点,并且不需要接触数据指针——交换指针的全部目的是重新排列数据


  • 当您使用单链表交换两个元素时,您应该从头到尾运行它,直到最后一个要交换的元素。在运行时,必须找到指向这两个必须交换的元素的前两个指针(如果元素是列表中的第一个,则前一个指针可能为空)。然后此循环完成(在要交换的秒元素处),您必须有四个元素:要交换的第一个元素(first)、第一个元素之前的元素(first_prev;可能为null)、第二个元素(second)和要交换的前一个元素(second_prev;可能为null)。然后你只需要像这样交换它们:

      temp_first_next = first->next;
      temp_second_next = second->next;
    
      /* put first element at place of the second */
      first->next = temp_second_next;
      if (second_prev) {
        second_prev->next = first;
      }
    
      /* put second element at place of the first */
      second->next = temp_first_next;
      if (first_prev) {
        first_prev->next = next;
      }
    

    如果你真的使用C++,最好切换到使用,并为每个元素使用类。这将更容易,指针更少。:)

    您不需要使用strcpy。交换节点时,可以通过交换.next成员变量来实现。使用链表的唯一原因是复制大量数据

    在这种情况下,把情况写在纸上是个好主意。在一条直线上画几个方框:

     W["1"] -> X["3"] -> Y["2"] -> Z["4"]
    
    好的,我们有四个节点,wxy和Z,设置为W指向X,X指向Y,Y指向Z

    每个节点都有一个字符串值。W的值是1,X的值是3,Y的值是2,Z的值是4

    我们希望对节点进行排序,以便字符串值按升序排列:1、2、3、4

    现在,您一直在尝试通过交换实际数据值来实现这一点:

    swap(X.data, Y.data)
    
     W["1"] -> Y["2"] -> X["3"] -> Z["4"]
    
    。。。导致:

    W["1"] -> X["2"] -> Y["3"] -> Z["4"]
    
    这会起作用,但这不是一个好主意,原因很多。比如说,它很容易出现bug。其次,您放弃了使用链表的主要好处:交换链接,而不是数据

    以下是交换节点链接时的情况:

    swap(X.data, Y.data)
    
     W["1"] -> Y["2"] -> X["3"] -> Z["4"]
    
    注意所有数据都没有移动——W的值仍然是1,X的值仍然是3,Y的值仍然是2,Z的值仍然是4。但是我们已经重新安排了遍历顺序:现在W指向Y,Y指向X,X指向Z,给出了预期的结果1,2,3,4

    这听起来可能令人困惑,但一旦你进入了正确的心态,这就很简单了。让我们一步一步地分解这些步骤

    我们的初始状态和

     W["1"] -> X["3"] -> Y["2"] -> Z["4"]
    
    。。。我们想要的状态呢

     W["1"] -> Y["2"] -> X["3"] -> Z["4"]
    
    啊哈,区别在于我们交换了中间的两个节点。这就是我们需要解决的操作:如何交换两个相邻的节点

    好吧,让我们这样看。W用来引出X,它引出Y,它引出Z

    我们想让W通向Y;我们希望Y引导X;我们想让X指向Z

    你看到了吗?这涉及修改三个节点。如果只知道其中两个节点,则无法执行此操作。您必须能够修改
    A -> A.next -> A.next.next -> A.next.next.next
    
    void swapAfter( Node* node ) {
      Node* A = node;
      Node* B = node->next;
      Node* C = node->next->next;
      Node* D = node->next->next->next;
    
      A->next = C; // Lead A to C
      C->next = B; // Lead C to B
      B->next = D; // Lead b to D
    
      // Now we have A->C->B->D!
    }
    
    Node* finalNode = nextNode->next;
    
    // right now, we have..
    //   prevNode -> curNode -> nextNode -> finalNode
    
    // we want..
    //   prevNode -> nextNode -> curNode -> finalNode
    
    // so..
    prevNode->next = nextNode;
    nextNode->next = curNode;
    curNode->next = finalNode;
    
    Person** people = new Person*[4];
    for ( int i = 0; i < 4; i++)
      people[i] = new Person();
    
    if ( strcmp(people[1]->fullName, people[2]->fullName) > 0 )
      swap( people[1], people[2] );