当使用单链表时,有人建议我使用“if x != nullptr”,但我不明白为什么这样做。在我的示例代码中,第一个和第二个打印语句有效,但是一旦我添加第三个打印语句,就会出现 nullptr 错误。尽管已经检查了之前的节点并成功运行了它。
ListNode* addTwoNumbers(ListNode* l1) { // l1 has 3 nodes containing a single digit
std::cout << l1->val;
l1 = l1->next;
if (l1 != nullptr) {
std::cout << l1->val;
}
std::cout << l1->val;
return 0;
}
如果注释掉第三条打印语句,它会打印出前两个节点,但如果添加第三条,则会出错。我已经检查过 if (l1 != nullptr) 并成功运行了第二个打印语句,因此可以安全地假设 l1->val 也有效,但事实并非如此。
对于生产工作,您应该使用标准库中的链表类之一。标题
<list>
(对于双链表)和 <forward_list>
(对于单链表)应该优先于任何类型的“自己动手”实现。
如果这是为了家庭作业,或者可能是某种编码挑战,你可能没有这个选择。但请注意,传递 naked
ListNode
指针并将它们视为列表是错误的做法。如果您必须推出自己的链接列表类,则应该按照标准库中的链接列表类进行建模。这意味着编写一个适当的 List
或 ForwardList
类来管理构成链表的 ListNodes
。 ListNode
结构本身应该嵌套在父 List
类中,并且您应该编写迭代器,这样您就不必使用原始指针。
这是一项相当大的工作。
然而,您可能无法选择“以正确的方式”做事。例如,LeetCode 使用裸
ListNode
指针,例如在 OP 中找到的指针。
OP 包含输出链表的代码。该代码失败了,因为它在检查指针是否等于
nullptr
之前取消引用指针。
这是使用
operator<<
的替代公式。
// main.cpp
#include <iostream>
#include <new>
#include <stdexcept>
#include <utility>
struct ListNode
{
int value{};
ListNode* next{ nullptr };
ListNode(
int const value = 0,
ListNode* const next = nullptr)
: value{ value }
, next{ next }
{}
static void clear(ListNode*& head) noexcept {
while (head)
pop_front(head);
}
static void pop_front(ListNode*& head) noexcept
{
if (head) {
auto p{ head };
head = head->next;
p->next = nullptr;
delete p;
}
}
static void push_front(ListNode*& head, int const n) {
head = new ListNode(n, head);
}
friend std::ostream& operator<< (std::ostream& ost, ListNode* const head)
{
ost.put('[');
if (auto node{ head }; node)
for (;;) {
ost << node->value;
node = node->next;
if (!node) break;
ost.put(',');
}
ost.put(']');
return ost;
}
};
int main()
{
ListNode* head{ nullptr };
ListNode::push_front(head, 1);
ListNode::push_front(head, 2);
ListNode::push_front(head, 3);
std::cout << "list = " << head << '\n';
ListNode::clear(head); // avoid memory leaks.
}
// end file: main.cpp
输出:
list = [3,2,1]