C++实现数据结构——链表

线性表的链式存储方式为链表(Linked List)

链表中的每个元素结点都需要保存以下两部分信息。

  1. 存储数据信息的部分,称为数据域。
  2. 存储前驱(prev/prior)或后继(next)结点的逻辑关系,称为指针域。

根据元素结点中指针域存储的指针个数和类型的不同,链表还可细分为单向链表、双向链表、单向环形链表(单向循环链表)、双向环形链表(双向循环链表)以及静态链表。

单向链表

如果元素结点只包含一个指针域,则称该链表为单向链表(Singly Linked List)。单向链表的结点结构如图所示。

SinglyLinkedList.h实现单向链表

复制代码
//
// 单向链表
//

#ifndef DS_SINGLYLINKEDLIST_H
#define DS_SINGLYLINKEDLIST_H

#include <iostream>
using namespace std;

template <class T>
//单向链表的节点结构
struct Node{
    T data;//数据
    Node* next;//指针,指向下一个节点的地址
    Node(const T& value, Node* n = nullptr) {
        data = value;
        next = n;
    }
};

template <class T>
class SinglyLinkedList {
    private:
        Node<T>* head;//始终指向链表头部的指针
    public:
        SinglyLinkedList():head(nullptr){}
        ~SinglyLinkedList() {
            auto* current = head;
            while (current != nullptr) {//通过head指针遍历链表并删除每个节点,直到链表为空
                auto next = current->next;
                delete current;//释放当前节点内存
                current = next;
            }
            head = nullptr;
        }
        //在链表头部添加一个元素
        void prepend(const T& data) {
            Node<T>* newNode = new Node(data);
            newNode->next = head;//新节点的next指针指向原头节点
            head = newNode;//更新头节点为新节点
        }
        //向链表末尾添加一个元素
        void append(const T& data) {
            Node<T>* newNode = new Node(data);
            if (head == nullptr) {
                head = newNode;
            } else {
                Node<T>* current = head;
                while (current->next != nullptr) {//通过next指针遍历至链表
                    current = current->next;
                }
                current->next = newNode;//链表末尾节点的next指针指向新节点
            }
        }
        //按data查找节点,返回指针
        Node<T>* findNode(const T& data) {
            auto currentNode = head;//从头节点开始遍历
            while (currentNode != nullptr) {
                if (currentNode->data == data) {
                    return currentNode;
                }
                currentNode = currentNode->next;//继续遍历下一个节点
            }
            return nullptr;//如果未找到返回nullptr
        }
        //按data删除节点
        void remove(const T& data) {
            if (head == nullptr) return;//链表为空直接返回

            if (head->data == data) {//如果头节点就是要删除的节点
                auto temp = head;
                head = head->next;//将原头节点的下一个节点更新为头节点
                delete temp;//释放原头节点的内存
                return;
            }

            auto current = head;
            while (current->next != nullptr) {//遍历当前节点的下一个节点
                if(current->next->data == data) {//找到要删除的节点
                    auto* temp = current->next;//保存要删除的节点指针
                    current->next = current->next->next;//变更当前节点的next指向
                    delete temp;//释放节点的内存
                    return;
                }
                current = current->next;
            }
        }


        //遍历所有元素
        void traverse() const {
            Node<T>* current = head;
            while (current != nullptr) {
                cout << current->data << " -> ";
                current = current->next;
            }
            cout << "nullptr" << endl;
        }
};
#endif //DS_SINGLYLINKEDLIST_H

main.cpp测试单向链表

复制代码
//
// 测试SinglyLinkedList
//

#include <iostream>

using namespace std;
#include "SinglyLinkedList.h"

int main() {
    SinglyLinkedList<string> sll;
    sll.append("b");
    sll.prepend("a");
    sll.append("c");
    sll.append("d");
    sll.append("e");
    sll.traverse();
    sll.remove("d");
    sll.traverse();
    sll.remove("a");
    sll.traverse();
    sll.remove("e");
    sll.traverse();
    cout << sll.findNode("c");

    return 0;
}

DoubleLinkedList.cpp实现双向链表

复制代码
//
// 双向链表
//

#ifndef DS_DOUBLELINKEDLIST_H
#define DS_DOUBLELINKEDLIST_H

#include <iostream>
using namespace std;

template <class T>
//双向链表的节点结构
struct Node{
    T data;//数据
    Node* prev;//指向前一个节点的指针
    Node* next;//指向后一个节点的指针

    Node(const T& value, Node* p = nullptr, Node* n = nullptr) {
        data = value;
        prev = p;
        next = n;
    }

};

template <class T>
class DoubleLinkedList {
private:
    Node<T>* head; //指向链表头部节点的指针
    Node<T>* tail; //指向链表尾部节点的指针
public:
    DoubleLinkedList() : head(nullptr), tail(nullptr) {}

    ~DoubleLinkedList() {
        while (head != nullptr) {
            Node<T>* current = head;
            head = head->next;
            delete current;
        }
        tail = nullptr;
    }

    // 在链表头部添加新节点
    void prepend(const T& data) {
        Node<T>* newNode = new Node(data);
        if (head == nullptr) { //如果链表为空,新节点同时是头节点和尾节点
            head = tail = newNode;
        } else {
            newNode->next = head;//新节点的next指向原头部节点
            head->prev = newNode;//原头部节点的prev指向新节点
            head = newNode;
        }
    }

    // 在链表末尾添加新节点
    void append(const T& data) {
        Node<T>* newNode = new Node(data);
        if (tail == nullptr) {//如果链表为空,新节点同时是头部节点和尾部节点
            head = tail = newNode;
        } else {
            tail->next = newNode;//原尾部节点的next指向新节点
            newNode->prev = tail;//新节点的prev指向原尾部节点
            tail = newNode;
        }
    }

    // 删除指定值的节点(只删除第一个匹配的节点)
    void remove(const T& data) {
        Node<T>* current = head;
        while (current != nullptr) {
            if (current->data == data) { //找到匹配的节点,进行删除操作
                if (current->prev != nullptr) { // 如果不是头节点,则重新链接前一个节点和后一个节点
                    current->prev->next = current->next;
                } else { // 是头节点的情况,更新头指针
                    head = current->next;
                }
                if (current->next != nullptr) { // 如果不是尾节点,则重新链接前一个节点和后一个节点
                    current->next->prev = current->prev;
                } else { // 是尾节点的情况,更新尾指针
                    tail = current->prev;
                }
                delete current; // 删除当前节点并退出循环(只删除第一个匹配的节点)
                return;
            }
            current = current->next; //继续搜索下一个节点
        }
        cout << "未找到值为 " << data << " 的节点" << endl;
    }

    //按data查找节点,返回指针
    Node<T>* findNode(const T& data) {
        Node<T>* currentNode = head;//从头节点开始遍历
        while (currentNode != nullptr) {
            if (currentNode->data == data) {
                return currentNode;
            }
            currentNode = currentNode->next;//继续遍历下一个节点
        }
        return nullptr;//如果未找到返回nullptr
    }
    //遍历所有元素
    void traverse() const {
        Node<T>* current = head;
        while (current != nullptr) {
            cout << current->data << " -> ";
            current = current->next;
        }
        cout << "nullptr" << endl;
    }
};

#endif //DS_DOUBLELINKEDLIST_H

main.cpp测试双向链表

复制代码
//
// 测试DoubleLinkedList
//

#include <iostream>

using namespace std;
#include "DoubleLinkedList.h"

int main() {
    DoubleLinkedList<string> dll;
    dll.append("b");
    dll.prepend("a");
    dll.append("c");
    dll.append("d");
    dll.append("e");
    dll.traverse();
    dll.remove("d");
    dll.traverse();
    dll.remove("a");
    dll.traverse();
    dll.remove("e");
    dll.traverse();
    cout << dll.findNode("c");

    return 0;
}

单向循环链表

通过引入哨兵节点,链表的操作(如插入、删除)可以统一处理,避免了对空链表或头节点的特殊处理。

静态链表在不支持指针的编程语言(如Fortran或某些嵌入式环境)中很有用,但现代主流语言(如C++)更倾向使用动态内存管理,所以这里不作讨论。

相关推荐
weisonx1 小时前
为什么要多写文章博客
java·c++
Ayanami_Reii1 小时前
进阶数据结构应用-SPOJ 3267 D-query
数据结构·算法·线段树·主席树·持久化线段树
__Ryan2 小时前
BlueprintImplementableEvent和BlueprintNativeEvent
c++·ue5·unreal engine
明洞日记2 小时前
【VTK手册019】 深入理解 vtkProperty:从几何表达到 PBR 物理渲染
c++·图像处理·算法·vtk·图形渲染
汉克老师2 小时前
2025年海淀区中小学信息学竞赛复赛(小学组试题第六题 蜂窝网络 (net))
c++·贪心算法·北京海淀中小学信息学竞赛·lower_bound
xiaoye-duck2 小时前
C++入门基础指南:命名空间namespace
c++
Genevieve_xiao3 小时前
【数据结构与算法】【xjtuse】面向考纲学习(下)
java·数据结构·学习·算法
4311媒体网3 小时前
php和c++哪个更好学?C++难学吗?
java·c++·php