简体   繁体   English

关于单链表的困惑

[英]Confusion about singly linked list

So I'm learning about linked lists recently. 所以我最近在学习链表。 The functions are somewhat straightforward, but when I checked the output, it's always messy. 这些功能有点简单,但是当我检查输出时,它总是很乱。

In the test file, from test 1 to test 3, I changed the position of std::cout lines, and in test 1 the output is not shown. 在测试文件中,从测试1到测试3,我改变了std :: cout线的位置,而在测试1中,输出没有显示。 I don't know how a simple cout line or the order of lines can affect how the linked list will be output. 我不知道简单的cout行或行的顺序如何影响链表的输出方式。 This is very confusing (details are provided in the output of each test) 这非常令人困惑(详细信息在每个测试的输出中提供)

My functions, specifically InsertHead, SearchList, InsertAfter, PreviousNode are sometimes correct in specific outputs. 我的函数,特别是InsertHead,SearchList,InsertAfter,PreviousNode有时在特定输出中是正确的。

For my InsertBefore function, I used a function called PreviousNode to get the pointer to previous node of current one, and used InsertAfter to insert a node after that previous node. 对于我的InsertBefore函数,我使用一个名为PreviousNode的函数来获取指向当前节点的前一个节点的指针,并使用InsertAfter在该前一个节点之后插入一个节点。 However, the result is infinite. 但是,结果是无限的。 (I'm not allowed to used doubly linked list) (我不允许使用双向链表)

File node.h 文件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;
}

File main.cpp, test 1, run InsertAfter functions: 文件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);
}

Output, test 1, the rest of the code is not outputted 输出,测试1,其余代码不输出

[90]->[80]->[70]->[60]->[50]->[40]->[30]->[20]->[10]->[0]->|||
Insert 500 after 50: 

File main.cpp, test 2: Run InsertAfter functions similarly like test 1 , but change the position of the std::cout lines: 文件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);
}

Output test 2: After changing the position of std::cout lines, the rest of the output is shown 输出测试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]->|||


File main.cpp test 3, Run InsertAfter like test 1 , but run only once: 文件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);
}

Output test 3, the output is shown: 输出测试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]->|||

File main.cpp test 4, run test 4, put InsertAfter like test 3 , then check PreviousNode 文件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);
}

Output: The previous node is 0, which is not correct 输出:前一个节点为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]


File main.cpp test 5, run InsertAfter and PreviousNode similarly like test 4 , but I run PreviousNode first. 文件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);
}

Output test 5, similar to test 4, but the output is correct: 输出测试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 test 6, run only InsertBefore 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);

}

Output test 6: The result appears infinitely 输出测试6:结果无限出现

[700]->[700]->[700]->[700]->[700]->

I sincerely hope you can look at it and explain to me why test 1 doesn't show the rest of the output, why PreviousNode in 4 because of minor changes in cout lines, and why the InsertBefore has a loop, even though I used only the previous functions. 我真诚地希望你能看一下并向我解释为什么测试1没有显示输出的其余部分,为什么4中的PreviousNode因为cout行的微小变化,以及为什么InsertBefore有一个循环,即使我只使用了以前的功能。 Thanks a lot! 非常感谢!

You must return the outs stream in operator<< . 您必须在operator<<返回outs流。 Currently you return nothing. 目前你没有回报。 It should be: 它应该是:

friend std::ostream& operator <<(std::ostream& outs, const Node<T> &printMe){
    outs << "[" << printMe._item << "]";
    return outs; // added missing return
}

Also, InsertAfter has a dangling pointer. 此外, InsertAfter有一个悬空指针。 Just watch gcc emit warnings (run all your compilations with -Wall on GCC and Clang, and with /w4 on Visual Studio): 只需看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;
      |     ^~~~

The offending code is: 违规代码是:

template <typename T>
Node<T>* InsertAfter(Node<T>* afterThis, const T& insertThis){
    // Create a temp node
    Node<T>* temp;
    temp->_item = insertThis;

The temp variable is a pointer, not a node. temp变量是指针,而不是节点。 Initially it points to nothing specific, and accessing it is undefined behavior. 最初它没有指明任何具体内容,访问它是未定义的行为。 You must create a new node: 您必须创建一个新节点:

template <typename T>
Node<T>* InsertAfter(Node<T>* afterThis, const T& insertThis){
    // Create a temp node
    auto temp = new Node<T>;
    temp->_item = insertThis;

With InsertBefore it is more complicated, since sometimes a new object is needed and sometimes it is not: 使用InsertBefore它会更复杂,因为有时需要一个新对象,有时它不是:

template <typename T>
Node<T>* InsertBefore(Node<T>*& head, Node<T>* beforeThis, T insertThis){
    Node<T> *prev = PreviousNode(head, beforeThis);

    Node<T>* temp;

So the safest thing is to reorganize the code a little: 所以最安全的是重新组织代码:

if (beforeThis != head){
    return InsertAfter(prev, insertThis);
}
auto temp = new Node<T>;

temp->_item = insertThis;
temp->_next = head;
head = temp;

General note : Better use std::unique_ptr and std::make_unique instead of raw pointers and new . 一般说明 :最好使用std::unique_ptrstd::make_unique而不是原始指针和new If possible, avoid new altogether. 如果可能的话,完全避免new If you use std::unique_ptr correctly, the chances for dangling pointers and memory leaks are considerably reduced. 如果正确使用std::unique_ptr ,则悬空指针和内存泄漏的可能性会大大降低。

Also, I strongly recommend to use C++ best practices. 另外,我强烈建议使用C ++最佳实践。 For example, hiding implementation details from the users of your class, using nullptr and not NULL , returning a reference when nullptr is impossible and there is no need to operate on the pointer, use return instead of modifying by reference parameters when possible, and so on. 例如,隐藏类的用户的实现细节,使用nullptr而不是NULL ,当nullptr不可能时返回引用,并且不需要对指针进行操作,使用return而不是在可能的情况下通过引用参数进行修改,等等上。

edit 编辑

Added an advice to consult warnings, when developing code. 在开发代码时添加了咨询警告的建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM