簡體   English   中英

關於單鏈表的困惑

[英]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_ptrstd::make_unique而不是原始指針和new 如果可能的話,完全避免new 如果正確使用std::unique_ptr ,則懸空指針和內存泄漏的可能性會大大降低。

另外,我強烈建議使用C ++最佳實踐。 例如,隱藏類的用戶的實現細節,使用nullptr而不是NULL ,當nullptr不可能時返回引用,並且不需要對指針進行操作,使用return而不是在可能的情況下通過引用參數進行修改,等等上。

編輯

在開發代碼時添加了咨詢警告的建議。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM