对于只存储头部而没有尾部的列表的插入(这意味着可以接受线性时间插入的小列表),您可以通过引入额外的指针间接来消除特殊情况:
简单版(指向节点的指针)
void List::push_back(int value)
{
// Point to the node link (pointer to pointer to node),
// not to the node.
Node** link = &head;
// While the link is not null, point to the next link.
while (*link)
link = &(*link)->next;
// Set the link to the new node.
*link = new Node(value, nullptr);
}
...您可以简化为:
void List::push_back(int value)
{
Node** link = &head;
for (; *link; link = &(*link)->next) {}
*link = new Node(value, nullptr);
}
相反,说:
复杂版本(指向节点的指针)
void List::push_back(int value)
{
if (head)
{
// If the list is not empty, walk to the back and
// insert there.
Node* node = head;
while (node->next)
node = node->next;
node->next = new Node(value, nullptr);
}
else
{
// If the list is empty, set the head to the new node.
head = new Node(value, nullptr);
}
}
或者说句公道话,去掉 cmets:
void List::push_back(int value)
{
if (head)
{
Node* node = head;
for (; node->next; node = node->next) {}
node->next = new Node(value, nullptr);
}
else
head = new Node(value, nullptr);
}
简单版无特殊情况
第一个版本不必对空列表进行特殊处理的主要原因是,如果我们想象head 为空:
Node** link = &head; // pointer to pointer to null
for (; *link; link = &(*link)->next) {}
*link = new Node(value, nullptr);
然后for 循环条件立即为假,然后我们将新节点分配给head。当我们使用指向指针的指针时,我们不必在循环之外单独检查这种情况。
插入排序
如果你想做一个插入排序而不是简单地插入到后面,那么这个:
void List::insert_sorted(int value)
{
Node** link = &head;
for (; *link && (*link)->value < value; link = &(*link)->next) {}
// New node will be inserted to the back of the list
// or before the first node whose value >= 'value'.
*link = new Node(value, *link);
}
性能
至于性能,不确定消除额外分支是否有很大不同,但它肯定会使代码更紧凑并降低其圈复杂度。 Linus 认为这种风格是“好品味”的原因是因为在 C 语言中,你经常不得不经常编写链表逻辑,因为泛化链表并不那么容易而且不一定值得,因为那里没有类模板,例如,所以它是方便以更小、更优雅、更不容易出错的方式来编写这些东西。另外,它表明您对指针的理解非常好。