[英]Confusion about singly linked list
所以我最近在学习链表。 这些功能有点简单,但是当我检查输出时,它总是很乱。
在测试文件中,从测试1到测试3,我改变了std :: cout线的位置,而在测试1中,输出没有显示。 我不知道简单的cout行或行的顺序如何影响链表的输出方式。 这非常令人困惑(详细信息在每个测试的输出中提供)
我的函数,特别是InsertHead,SearchList,InsertAfter,PreviousNode有时在特定输出中是正确的。
对于我的InsertBefore函数,我使用一个名为PreviousNode的函数来获取指向当前节点的前一个节点的指针,并使用InsertAfter在该前一个节点之后插入一个节点。 但是,结果是无限的。 (我不允许使用双向链表)
文件node.h
#include <iostream>
using namespace std;
template <typename T>
struct Node{
T _item;
Node<T>* _next;
Node() {
_item = T();
_next = NULL;
}
Node(T item){
_item = item;
_next = NULL;
}
// Print the value of a node
friend std::ostream& operator <<(std::ostream& outs, const Node<T> &printMe){
outs << "[" << printMe._item << "]";
}
};
// Print the entire linked list
template <typename T>
void PrintList(Node<T>* head){
Node<T>* walker = head;
while(walker != NULL){
cout << *walker;
cout << "->";
walker = walker->_next;
}
cout << "|||";
}
// Insert an item to the head
template <typename T>
Node<T>* InsertHead(Node<T>* &head, const T& item){
Node<T>* temp = new Node<T>(item);
temp->_next = head;
head = temp;
return head;
}
// Search an element in list, return the pointer to that node
template <typename T>
Node<T>* SearchList(Node<T>* head, const T& item){
Node<T>* temp = head;
// Iterate temp to find the match item
while (temp->_item != item && temp->_next != NULL)
temp = temp->_next;
if (temp->_item == item) // If found, return temp
return temp;
else
return NULL;
}
// find previous node
template <typename T>
Node<T>* PreviousNode(Node<T>* head, Node<T>* prevToThis) {
if (prevToThis == head)
return NULL;
else {
Node<T> *prev = head;
// Iterate it until it reaches the one before prevToThis
while(prev->_next != NULL && prev->_next != prevToThis)
prev = prev->_next;
return prev;
}
}
template <typename T>
Node<T>* InsertAfter(Node<T>* afterThis, const T& insertThis){
// Create a temp node
Node<T>* temp;
temp->_item = insertThis;
if (afterThis->_next == NULL){
temp->_next = NULL;
afterThis->_next = temp;
}
else {
// Point temp to next node
temp->_next = afterThis->_next;
// Point mark node to temp
afterThis->_next = temp;
}
return temp;
}
// Insert an item before a node
template <typename T>
Node<T>* InsertBefore(Node<T>*& head, Node<T>* beforeThis, T insertThis){
Node<T> *prev = PreviousNode(head, beforeThis);
Node<T>* temp;
// If current node is head node
if (beforeThis == head){
temp->_item = insertThis;
temp->_next = head;
head = temp;
}
// Other nodes
else {
temp = InsertAfter(prev, insertThis);
}
return temp;
}
文件main.cpp,测试1,运行InsertAfter函数:
int main(){
Node<int>* head = NULL;
for (int i = 0; i < 10; i++)
InsertHead(head, i * 10);
PrintList(head);
cout << endl;
Node<int> *pos_50 = SearchList(head, 50);
cout << "Insert 500 after 50: ";
cout << endl;
InsertAfter(pos_50, 500);
PrintList(head);
Node<int> *pos_0 = SearchList(head, 0);
cout << "Insert 600 after 0: ";
cout << endl;
InsertAfter(pos_0, 600);
PrintList(head);
}
输出,测试1,其余代码不输出
[90]->[80]->[70]->[60]->[50]->[40]->[30]->[20]->[10]->[0]->|||
Insert 500 after 50:
文件main.cpp,测试2:像测试1一样运行InsertAfter函数,但更改std :: cout行的位置:
int main(){
Node<int>* head = NULL;
for (int i = 0; i < 10; i++)
InsertHead(head, i * 10);
PrintList(head);
cout << endl;
cout << "Insert 500 after 50: ";
cout << endl;
Node<int> *pos_50 = SearchList(head, 50);
InsertAfter(pos_50, 500);
PrintList(head);
cout << endl;
cout << "Insert 600 after 0: ";
cout << endl;
Node<int> *pos_0 = SearchList(head, 0);
InsertAfter(pos_0, 600);
PrintList(head);
}
输出测试2:更改std :: cout行的位置后,显示输出的其余部分
[90]->[80]->[70]->[60]->[50]->[40]->[30]->[20]->[10]->[0]->|||
Insert 500 after 50:
[90]->[80]->[70]->[60]->[50]->[500]->[40]->[30]->[20]->[10]->[0]->|||
Insert 600 after 0:
[90]->[80]->[70]->[60]->[50]->[500]->[40]->[30]->[20]->[10]->[0]->[600]->|||
文件main.cpp测试3,像测试1一样运行InsertAfter,但只运行一次:
int main() {
Node<int>* head = NULL;
for (int i = 0; i < 10; i++)
InsertHead(head, i * 10);
PrintList(head);
cout << endl;
Node<int> *pos_50 = SearchList(head, 50);
cout << "Insert 500 after 50: ";
cout << endl;
InsertAfter(pos_50, 500);
PrintList(head);
}
输出测试3,输出显示:
[90]->[80]->[70]->[60]->[50]->[40]->[30]->[20]->[10]->[0]->|||
Insert 500 after 50:
[90]->[80]->[70]->[60]->[50]->[500]->[40]->[30]->[20]->[10]->[0]->|||
文件main.cpp测试4,运行测试4,将InsertAfter放入测试3 ,然后检查PreviousNode
int main() {
Node<int>* head = NULL;
for (int i = 0; i < 10; i++)
InsertHead(head, i * 10);
PrintList(head);
cout << endl;
cout << "Insert 500 after 50: ";
cout << endl;
Node<int> *pos_50 = SearchList(head, 50);
InsertAfter(pos_50, 500);
PrintList(head);
cout << "Previous node before 50: " << *PreviousNode(head, pos_50);
}
输出:前一个节点为0,这是不正确的
[90]->[80]->[70]->[60]->[50]->[40]->[30]->[20]->[10]->[0]->|||
Insert 500 after 50:
[90]->[80]->[70]->[60]->[50]->[500]->[40]->[30]->[20]->[10]->[0]->|||
Previous node before 50: [0]
文件main.cpp测试5,运行InsertAfter和PreviousNode类似于测试4 ,但我先运行PreviousNode。
int main(){
Node<int>* head = NULL;
for (int i = 0; i < 10; i++)
InsertHead(head, i * 10);
PrintList(head);
cout << endl;
Node<int> *pos_50 = SearchList(head, 50);
cout << "Previous node before 50: " << *PreviousNode(head, pos_50);
cout << endl;
cout << "Insert 500 after 50: ";
cout << endl;
InsertAfter(pos_50, 500);
PrintList(head);
}
输出测试5,类似于测试4,但输出正确:
[90]->[80]->[70]->[60]->[50]->[40]->[30]->[20]->[10]->[0]->|||
Previous node before 50: [60]
Insert 500 after 50:
[90]->[80]->[70]->[60]->[50]->[500]->[40]->[30]->[20]->[10]->[0]->|||
Main.cpp测试6,只运行InsertBefore
int main(){
Node<int>* head = NULL;
for (int i = 0; i < 10; i++)
InsertHead(head, i * 10);
PrintList(head);
cout << endl;
Node<int> *pos_50 = SearchList(head, 50);
cout << "Insert 700 before 50: " << endl;
InsertBefore(head, pos_50, 700);
PrintList(head);
}
输出测试6:结果无限出现
[700]->[700]->[700]->[700]->[700]->
我真诚地希望你能看一下并向我解释为什么测试1没有显示输出的其余部分,为什么4中的PreviousNode因为cout行的微小变化,以及为什么InsertBefore有一个循环,即使我只使用了以前的功能。 非常感谢!
您必须在operator<<
返回outs
流。 目前你没有回报。 它应该是:
friend std::ostream& operator <<(std::ostream& outs, const Node<T> &printMe){
outs << "[" << printMe._item << "]";
return outs; // added missing return
}
此外, InsertAfter
有一个悬空指针。 只需看gcc
发出警告(在GCC和Clang上使用-Wall运行所有编译,在Visual Studio上使用/ w4运行):
prog.cc: In function 'Node<T>* InsertAfter(Node<T>*, const T&) [with T = int]':
prog.cc:83:5: warning: 'temp' is used uninitialized in this function [-Wuninitialized]
83 | temp->_item = insertThis;
| ^~~~
违规代码是:
template <typename T>
Node<T>* InsertAfter(Node<T>* afterThis, const T& insertThis){
// Create a temp node
Node<T>* temp;
temp->_item = insertThis;
temp
变量是指针,而不是节点。 最初它没有指明任何具体内容,访问它是未定义的行为。 您必须创建一个新节点:
template <typename T>
Node<T>* InsertAfter(Node<T>* afterThis, const T& insertThis){
// Create a temp node
auto temp = new Node<T>;
temp->_item = insertThis;
使用InsertBefore
它会更复杂,因为有时需要一个新对象,有时它不是:
template <typename T>
Node<T>* InsertBefore(Node<T>*& head, Node<T>* beforeThis, T insertThis){
Node<T> *prev = PreviousNode(head, beforeThis);
Node<T>* temp;
所以最安全的是重新组织代码:
if (beforeThis != head){
return InsertAfter(prev, insertThis);
}
auto temp = new Node<T>;
temp->_item = insertThis;
temp->_next = head;
head = temp;
一般说明 :最好使用std::unique_ptr
和std::make_unique
而不是原始指针和new
。 如果可能的话,完全避免new
。 如果正确使用std::unique_ptr
,则悬空指针和内存泄漏的可能性会大大降低。
另外,我强烈建议使用C ++最佳实践。 例如,隐藏类的用户的实现细节,使用nullptr
而不是NULL
,当nullptr
不可能时返回引用,并且不需要对指针进行操作,使用return
而不是在可能的情况下通过引用参数进行修改,等等上。
在开发代码时添加了咨询警告的建议。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.