在单向链表中插入节点 C++

Jinku Hu 2023年1月30日 2021年6月28日
  1. 实现在链表末尾插入节点的函数
  2. 实现一个函数在链表中的给定节点之后插入一个节点
  3. 实现在链表开头插入节点的函数
在单向链表中插入节点 C++

本文讲解了在 C++ 中如何在单向链表中插入新节点的方法。

实现在链表末尾插入节点的函数

链表是线性数据结构,由依次指向彼此的节点组成。在本文中,我们更多地关注单链表结构并相应地实现多个插入操作。

在单链表中,我们有一个数据对象和一个指向列表中下一个节点的指针。我们定义了一个名为 ListNode 的节点结构和两个辅助函数(freeNodesprintNodes),以更好地演示列表插入操作。请注意,插入操作的时间复杂度因我们将节点插入到的位置而异。例如,如果列表末尾未知,则在列表末尾插入需要线性时间。

另一方面,在开头插入一个新节点总是需要固定的时间。下面的代码演示了 insertNodeEnd 函数,它可以被视为构建列表的核心函数。它将列表的头部作为第一个参数和需要存储在新节点上的字符串数据。该函数可以创建列表中的第一个元素并在其末尾附加新元素。该函数在空闲存储内存上分配新节点。因此,需要 freeNodes 函数在程序退出之前释放所有内存。

#include <iostream>
#include <string>
#include <utility>
#include <vector>

using std::cout; using std::cin;
using std::endl; using std::string;
using std::vector;

struct ListNode {
    struct ListNode *next{};
    string data;
};

struct ListNode *insertNodeEnd(struct ListNode *root, string data) {
    auto new_node = new ListNode;
    if (root) {
        while (root->next)
            root = root->next;

        new_node->next = nullptr;
        new_node->data = std::move(data);
        root->next = new_node;

        return root->next;
    }
    new_node->next = nullptr;
    new_node->data = std::move(data);
    return new_node;
}

void freeNodes(struct ListNode *root) {
    struct ListNode *tmp = nullptr;
    while (root) {
        tmp = root;
        root = root->next;
        delete tmp;
    }
}

void printNodes(struct ListNode *node) {
    auto count = 0;
    while (node){
        cout << "node " << count << " - data: " << node->data << endl;
        node = node->next;
        count++;
    }
}

int main() {
    struct ListNode *head = nullptr;

    vector<string> data_set = { "Precise", "Quantal",
                                "Saucy",  "Raring"};

    head = insertNodeEnd(head, data_set.at(0));
    for (auto it = data_set.begin() + 1; it != data_set.end(); ++it) {
        insertNodeEnd(head, *it);
    }
    printNodes(head);
    cout << " ----------------------------------- " << endl;

    insertNodeEnd(head, "Utopic");
    printNodes(head);

    freeNodes(head);
    return EXIT_SUCCESS;
}

输出:

node 0 - data: Precise
node 1 - data: Quantal
node 2 - data: Saucy
node 3 - data: Raring
 -----------------------------------
node 0 - data: Precise
node 1 - data: Quantal
node 2 - data: Saucy
node 3 - data: Raring
node 4 - data: Utopic

实现一个函数在链表中的给定节点之后插入一个节点

在列表中间插入一个节点通常需要常数时间加上用于找到给定位置的搜索算法的时间复杂度。尽管如此,我们假设搜索函数是单独实现的,并构造了 insertNodeAfter 函数,以便需要将目标节点的位置作为第一个参数提供。

由于 insertNodeEnd 函数返回指向新添加节点的指针,我们利用其返回值来演示 insertNodeAfter 的操作。请记住,对于任意位置插入,你需要一个单独的搜索功能,甚至可能需要一个外部数据结构来在链表上实现更快的搜索操作。

#include <iostream>
#include <string>
#include <utility>
#include <vector>

using std::cout; using std::cin;
using std::endl; using std::string;
using std::vector;

struct ListNode {
    struct ListNode *next{};
    string data;
};

struct ListNode *insertNodeEnd(struct ListNode *root, string data) {
    auto new_node = new ListNode;
    if (root) {
        while (root->next)
            root = root->next;

        new_node->next = nullptr;
        new_node->data = std::move(data);
        root->next = new_node;

        return root->next;
    }
    new_node->next = nullptr;
    new_node->data = std::move(data);
    return new_node;
}

struct ListNode *insertNodeAfter(struct ListNode *prev, string data) {
    if (!prev)
        return nullptr;

    auto new_node = new ListNode;
    new_node->next = nullptr;
    new_node->data = std::move(data);
    prev->next = new_node;
    return new_node;
}

void freeNodes(struct ListNode *root) {
    struct ListNode *tmp = nullptr;
    while (root) {
        tmp = root;
        root = root->next;
        delete tmp;
    }
}

void printNodes(struct ListNode *node) {
    auto count = 0;
    while (node){
        cout << "node " << count << " - data: " << node->data << endl;
        node = node->next;
        count++;
    }
}

int main() {
    struct ListNode *head = nullptr;

    vector<string> data_set = { "Precise", "Quantal",
                                "Saucy",  "Raring"};

    head = insertNodeEnd(head, data_set.at(0));
    for (auto it = data_set.begin() + 1; it != data_set.end(); ++it) {
        insertNodeEnd(head, *it);
    }
    printNodes(head);
    cout << " ----------------------------------- " << endl;

    auto iter = insertNodeEnd(head, "Utopic");
    printNodes(head);
    cout << " ----------------------------------- " << endl;

    insertNodeAfter(iter, "Xenial");
    printNodes(head);

    freeNodes(head);
    return EXIT_SUCCESS;
}

输出:

node 0 - data: Precise
node 1 - data: Quantal
node 2 - data: Saucy
node 3 - data: Raring
 -----------------------------------
node 0 - data: Precise
node 1 - data: Quantal
node 2 - data: Saucy
node 3 - data: Raring
node 4 - data: Utopic
 -----------------------------------
node 0 - data: Precise
node 1 - data: Quantal
node 2 - data: Saucy
node 3 - data: Raring
node 4 - data: Utopic
node 5 - data: Xenial

实现在链表开头插入节点的函数

单向链表的另一个有用的插入操作是在开头附加一个新节点。该函数具有最佳运行时间 O(1),因为始终存储列表的头部以访问列表本身。insertNodeFront 函数获取对根指针和需要存储在节点上的 string 对象的引用。该过程被实现,以便你可以使用它来初始化一个新的链表以及前插入。

或者,你可以重写函数以在 root 参数不是 nullptr 时分配一个新节点。否则,返回 nullptr 表示函数失败。这些函数的接口取决于程序员的需要和 ListNode 的结构。

#include <iostream>
#include <string>
#include <utility>
#include <vector>

using std::cout; using std::cin;
using std::endl; using std::string;
using std::vector;

struct ListNode {
    struct ListNode *next{};
    string data;
};

struct ListNode *insertNodeEnd(struct ListNode *root, string data) {
    auto new_node = new ListNode;
    if (root) {
        while (root->next)
            root = root->next;

        new_node->next = nullptr;
        new_node->data = std::move(data);
        root->next = new_node;

        return root->next;
    }
    new_node->next = nullptr;
    new_node->data = std::move(data);
    return new_node;
}

struct ListNode *insertNodeFront(struct ListNode *&root, string data) {
    auto new_node = new ListNode;
    if (root) {
        new_node->next = root;
        new_node->data = std::move(data);
        root = new_node;
        return root;
    }
    new_node->next = nullptr;
    new_node->data = std::move(data);
    return new_node;
}

void freeNodes(struct ListNode *root) {
    struct ListNode *tmp = nullptr;
    while (root) {
        tmp = root;
        root = root->next;
        delete tmp;
    }
}

void printNodes(struct ListNode *node) {
    auto count = 0;
    while (node){
        cout << "node " << count << " - data: " << node->data << endl;
        node = node->next;
        count++;
    }
}

int main() {
    struct ListNode *head = nullptr;

    vector<string> data_set = { "Precise", "Quantal",
                                "Saucy",  "Raring"};

    head = insertNodeEnd(head, data_set.at(0));
    for (auto it = data_set.begin() + 1; it != data_set.end(); ++it) {
        insertNodeEnd(head, *it);
    }
    printNodes(head);
    cout << " ----------------------------------- " << endl;

    insertNodeFront(head, "Bionic");
    printNodes(head);

    freeNodes(head);
    return EXIT_SUCCESS;
}

输出:

node 0 - data: Precise
node 1 - data: Quantal
node 2 - data: Saucy
node 3 - data: Raring
 -----------------------------------
node 0 - data: Bionic
node 1 - data: Precise
node 2 - data: Quantal
node 3 - data: Saucy
node 4 - data: Raring
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn

相关文章 - C++ Data Structure