我想在此页面上试用链接列表的示例:How to create Linked List using C++ 到目前为止,这是我的代码:
#include <iostream>
using namespace s-td;
class node {
public:
int value;
node *next;
};
int main() {
node *head = nullptr;
node *temp1;
temp1 = new node();
temp1->value = 1;
temp1 = head;
while(temp1->next!=nullptr) {
temp1 = temp1->next;
}
return 0;
}
但不幸的是我得到了Segmentation fault
。
我认为这部分是错误的:
while(temp1->next!=nullptr) {
temp1 = temp1->next;
}
答案 0 :(得分:3)
您的代码有两个问题:
a)您正在分配temp1 = head
,这与您案件中temp1 = nullptr
的同义词
b)您没有将->next
初始化为nullptr
,因此您的时间条件可能会或可能不会得到满足(未定义的行为)
答案 1 :(得分:0)
node *head = nullptr;
head
是NULL
temp1 = head;
temp1
变为NULL
while(temp1->next!=nullptr) {
temp1
为NULL
,因此temp1->next
取消引用NULL
指针。
答案 2 :(得分:0)
您的程序使用空head
指针开始迭代列表。如果你在调试器中查看它,head仍然是NULL,因为你从未设置它。
node *temp1, *temp2, *temp3;
temp1 = new node();
temp1->value = 1;
temp1->next = nullptr;
head= temp; // link to head
temp2 = new node();
temp2->value = 2;
temp2->next = nullptr;
temp1->next= temp2; // link 1 -> 2
temp3 = new node();
temp3->value = 3;
temp3->next = nullptr;
temp2->next= temp3; // link 2 -> 3
// now we have head -> temp1 -> temp2 -> temp3
// now use temp1 as iterator starting with head
temp1 = head;
while(temp1->next!=nullptr) {
printf("%d\n", temp1->value);
temp1 = temp1->next;
}
答案 3 :(得分:0)
您正在分配:
node *head = 0;
// ...
temp1 = head;
while(temp1->next!=nullptr) { // temp1 is null here
temp1 = temp1->next;
}
此外temp1->next
也不会为空。你应该初始化它:
#define NULL 0
class node {
public:
node(int value, node *next) {
this.value = value;
this.next = next;
}
int value;
node *next;
};
// ..
node *n3 = new node(1, NULL);
node *n2 = new node(1, n3);
node *n1 = new node(1, n2);
答案 4 :(得分:0)
该文给出了如何在C中创建链接列表的合理解释,它使用了C ++注释,但它是如何在C ++中创建链表的可怕解释。
让我带你离开那里的C-mindset,让你走上封装的道路。
通常,链表中的每个元素都称为“节点”,并且对于每个节点,都有一个指向列表中一个或多个其他节点的指针。在“单链表”中,它是到下一个节点,在“双向链表”中有一个指向之前节点和节点之后的指针,以允许我们向后和向前。
一个重要的早期决定是如何封装这个指向。你可以选择一个简单的“Node”类,它是List类的私有类,它有prev / next指针和一个指向实际数据本身的离散“void *”指针。这是一种非常类似于C的方法,因为它抛弃了类型信息。啊 - 但你可以将类型存储为枚举或什么?你可以,这是一个很棒的C解决方案。
但是C ++完全是关于封装的。作为List的节点是一个明确定义且相当离散的角色,具有一组简单的属性和属性。它非常适合封装为一个简单的基类。
class ListNode
{
ListNode* m_next;
ListNode() : m_next(NULL) {}
ListNode* next() { return m_next; }
// return the last node in my current chain.
ListNode* tail() {
ListNode* node = this;
while (node->next() != nullptr)
node = node->next();
return node;
}
// insert a new node at this point in the chain,
// think about what happens if newNode->next() is not null tho.
bool linkTo(ListNode* newNode);
};
同样重要的封装问题是,您是否希望任何人能够出现并调用ListNode成员,或者您是否希望将其可见性限制为List本身的访问者。当然有一些模式可以帮助处理抽象的“ListNode *”而无需知道它们属于哪个列表。但是单链表有限制 - 例如,在不知道列表本身的情况下,你不能删除一个条目(你怎么知道谁指向你?)
然而,这允许我们做
class Product : public ListNode {
string m_name;
std::vector<float> m_priceHistory;
public:
Product(const std::string& name_)
: ListNode()
, m_name(name_)
, m_priceHistory()
{}
void save();
};
class Book : public Product { ... };
class DVD : public Product { ... };
List productList;
productList.add(new Book("hello world"));
productList.add(new DVD("lose wait: lose a limb"));
productList.add(new Insurance("dietary related gangrene"));
...
for(ListNode* node = productList.head(); node != nullptr; node = node->next()) {
node->save();
}
另一种方法会让我们回到第一个ListNode想法,更像C一个;问题不在于它使用了指针,而是它丢弃了类型信息。当你想说“这个指针的类型不是”时,“void”主要用于此。缺点是,“这个指针的类型消失了”。我们可以使用模板解决这个问题。
template<typename T> // T will be an alias for whatever type we have to deal with.
class List
{
struct Node* m_list;
public:
struct Node
{
Node* m_next;
T* m_data;
Node() : m_next(nullptr), m_data(nullptr) {}
Node* next() const { return m_next; }
bool linkTo(Node* newPredecessor);
bool link(Node* newFollower);
};
public:
List() : m_list(nullptr) {}
Node* head() { return m_next; }
Node* tail() {
if (m_list == nullptr)
return nullptr;
for (Node* node = m_list; node->m_next != nullptr; node = node->m_next)
{}
return node;
}
void insert(T* data) { // add to head of the list
Node* node = new Node(data);
node->m_next = m_head;
m_head = node;
}
Node* append(T* data) {
if (head() == nullptr)
insert(data);
else {
Node* node = new Node(data);
Node* tail = this->tail(); // could get expensive.
tail->link(node);
}
return node;
}
};
List<Product> productList;
productList.append(new Book("Gone with the money"));
productList.append(new DVD("that's no moon"));
productList.append(new Pet("llama"));
这种方法的一个优点是我们不必在数据定义中添加额外的成员/混乱。缺点是我们使用更多内存 - 每个节点需要两个指针,并且节点没有简单的方法来告诉您它们在列表中是否/在哪里(您必须搜索所有节点并找到指向您的节点项)。
在内存分配方面,还有一个使用率确定的成本/收益。
这个最新的迭代还有一个主要的C-esque组件,Node类太明显了。理想情况下,它应该对最终用户完全私有。但由于数据元素无法确定它们在列表中的位置,因此无法遍历列表。
你想要的是隐藏节点定义,这样只有列表可以看到它(不,没关系,你真的不需要改变'm_next'的能力)并创建一个提供我们功能的二级类不需要让我们做任何我们不应该做的事情。
public:
class Iterator
{
Node* m_node;
public:
Iterator(Node* node_=nullptr) : m_node(node_) {}
bool advance() {
if (isValid())
m_node = m_node->m_next;
return isValid();
}
T* data() {
return isValid() ? m_node->m_data : nullptr;
}
bool isValid() const { return (m_node != nullptr); }
bool isTail() const { return isValid() && (m_node->m_next != nullptr); }
// if you feel the need to get seriously advanced,
// overload operator "++" to allow ++it;
// overload operator "*" to allow (*it) to get m_data
// overload operator "=" to allow Iterator* it = list->head();
};
// in class list:
Iterator head() { return Iterator(m_head); }
Iterator tail() {
Iterator it(m_head);
while (!it.isTail()) {
it.advance();
}
return it;
}
Iterator find(const T& data_) { return find(&data_); }
Iterator find(const T* data_) {
for (Iterator it = head(); it.isValid(); it.advance()) {
if(it.data() == data_)
return it;
}
}
希望这足以给你很多想法:)