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] );