插入节点。首先...代码!
#include <iostream>
struct listItem
{
//creating a node
int data;
struct listItem *next;
};
//function to insert items
void Insert(listItem *&head, int item)
{
listItem **curr = &head; // start at head
while (*curr != NULL // while there is a node
&& (*curr)->data <= item ) // and the node goes before the new node
{
curr = &(*curr)->next; // get next node
}
*curr = new listItem{item, *curr}; // insert the new node
}
int main()
{
listItem * head = NULL; // empty linked list head
Insert(head, 1); // test insert to empty list
Insert(head, 0); // insert at head
Insert(head, 3); // insert at end
Insert(head, 2); // insert in the middle
}
我删除了所有不必要的代码,以专注于插入逻辑。您应该始终针对堆栈溢出问题执行此操作。为了降低问题中的噪音,请创建一个简单的程序来说明问题,而不执行其他操作。读最小可重复示例 https://stackoverflow.com/help/minimal-reproducible-example获取更多信息和灵感。
代码几乎是不言自明的:循环遍历列表,直到找到插入节点的位置,然后插入节点,但有两个小技巧。
有的是head
节点并且有next
节点。两者都执行相同的工作:指向列表中的下一个节点。由于它们有不同的名称,我们需要不同的代码来处理它们。但如果我们可以使head
和所有的next
节点具有相同的名称?然后他们就可以拥有完全相同的代码。那是curr
的工作。现在没关系curr
is head
or a next
。该函数的大部分代码都......消失了。不存在的代码就没有错误。
插入单链表的另一个问题是,如果您迭代到需要在前面插入的节点,那么您就会失去对之前节点的跟踪。要维持链接,您必须拥有前一个节点的next
指针(或head
)。您可以维护指向前一个节点的指针,但这不适用于head
因为没有head
节点,所以你破坏了第一个技巧并最终得到一些几乎重复的代码。但是如果你抽象出节点并存储地址head
or the next
指针将两部分信息合而为一:插入点和插入点之后的节点。和
listItem **curr;
curr
指向我们想要放置新节点的位置*curr
是它后面的节点。所以
while (*curr != NULL // while there is a node
&& (*curr)->data <= item ) // and the node goes before the new node
{
curr = &(*curr)->next; // get next node
}
遍历列表直到找到列表中的最后一个节点,*curr
is NULL
,或数据*curr
走到我们要插入的节点之后,然后
*curr = new listItem{item, *curr};
创建一个链接到后面的节点的新节点,并将该新节点分配给插入点处的指针。
*curr
,指向列表中下一项的指针,被设置为指向new
listItem
,就像任何其他使用一样new
.
扭曲的是listItem
是一个非常简单的数据结构,可以利用聚合初始化 https://en.cppreference.com/w/cpp/language/aggregate_initialization初始化listItem
的成员。大括号包含我想要的新值listItem
按照它们在结构中声明的顺序。第一个成员变量,data
, 被设定为item
第二个,next
,设置为当前值*curr
,列表中的下一项。
在一小段时间里,你将拥有*curr
和next
新的listItem
指着同一个listItem
那么=
运营商更新*curr
指向新创建的listItem
反而。
把它画在一张纸上,你就会看到发生了什么。