美文网首页
C++ 单链表(带虚拟头节点)

C++ 单链表(带虚拟头节点)

作者: 我阿郑 | 来源:发表于2022-01-07 15:08 被阅读0次

为了操作方便,统一所有节点的处理逻辑,可以在最前面增加一个没有实际含义的 虚拟头结点 ,这个虚拟头结点不存储有效数据

image.png

不论是带 虚拟头结点的链表还是不带 虚拟头结点的链表,头指针first都指向链表中的第一个结点

  • 如果该链表有虚拟头结点,则头指针first指向虚拟头结点
  • 如果没有虚拟头结点,则头指针first指向链表的第一个节点

虚拟头结点 的单向链表与普通单向链表代码类似:但是 add、reomove 略有不同

#ifndef SingleLinkedList_hpp
#define SingleLinkedList_hpp

#include <iostream>
using namespace std;
#define ELEMENT_NOT_FOUND  -1

template <class T>
class SingleLinkedList;

template <class T>
class SNode {
    // SingleLinkedList<T> 是SNode<T>的一个友类,所以 SingleLinkedList<T> 可以访问SNode<T>的所有成员(尤其是私有成员)
    friend SingleLinkedList<T>;
public:
    SNode(T data, SNode<T> *next):m_data(data), m_next(next) {
        
    }
private:
    T m_data;
    SNode<T> *m_next;
};


template <class T>
class SingleLinkedList {
public:
    SingleLinkedList() {
        // 初始化一个虚拟头结点
        m_first = new SNode<T>(0, nullptr);
    }
    ~SingleLinkedList() {
        // 析构函数,用于删除链表中的所有节点(包括虚拟头节点)
        SNode<T> *node;
        while (m_first != nullptr) {
            node = m_first->m_next;
            delete m_first;
            m_first = node;
        }
    }
    
    // 在index位置插入元素
    SingleLinkedList<T>& add(const int index, const T& data) {
        rangeCheckForAdd(index);
        SNode<T> *prev = nullptr;
        if (index == 0) {
            prev = m_first;
        } else {
            prev = node(index - 1);
        }
        prev->m_next = new SNode<T>(data, prev->m_next);
        m_size++;
        return *this;
    }
    
    SingleLinkedList<T>& add(const T& data) {
        add(m_size, data);
        return *this;
    }
    
    SingleLinkedList<T>& remove(const int index, T&data) {
        rangeCheck(index);
        SNode<T> *prev = nullptr;
        if (index == 0) {
            prev = m_first;
        } else {
            prev = node(index - 1);
        }
    
        // SNode<T> *node = node(index - 1); 下面的写法更好
        SNode<T> *node = prev->m_next;
        prev->m_next = node->m_next;
        data = node->m_data;
        delete node;
        m_size--;
        return *this;
    }
    
    // 删除链表中的所有节点(不包括虚拟头节点)
    void clear() {
        SNode<T> *node;
        while(m_first->m_next != nullptr) {
            node = m_first->m_next;
            delete m_first;
            m_first = node;
        }
        m_size = 0;
    }
    
    // 获取index位置的元素
    // 因为要给data赋值所以这里不能写成 const T& data
    void get(int index, T& data) const {
        SNode<T> *temp = node(index);
        if (temp) {
            data = temp->m_data;
        }
    }
    
    // 更新index位置的元素
    void set(int index, const T& data) const {
        SNode<T> *temp = node(index);
        if (temp) {
            temp->m_data = data;
        }
    }
    
    // 查找元素的索引
    int indexOf(const T& data) {
        SNode<T> *current = m_first->m_next;
        int index = 0;
        while (current && current->m_data != data) {
            current = current->m_next;
            index++;
        }
        if (current) {
            return index;
        }
        return ELEMENT_NOT_FOUND;
    }
    
    bool isEmpty() const {
        return m_size == 0;
    }
    
    int size() const {
        return m_size;
    }
    
    SNode<T>* node(const int index) const {
        rangeCheck(index);
        // m_first->m_next 因为第一个是无效的虚拟头节点
        SNode<T> *node = m_first->m_next;
        for (int i = 0; i < index; i++) {
            node = node->m_next;
        }
        return node;
    }
    
    void output(ostream& out) const {
        SNode<T> *node = m_first->m_next;
        while (node) {
            out << node->m_data << ", ";
            node = node->m_next;
        }
    }
    
private:
    SNode<T> *m_first; // 指向第一个节点的指针
    int m_size;
    
    void outOfBounds(int index) const {
        throw index;
    }
    
    void rangeCheck(int index) const {
        if (index < 0 || index >= m_size) {
            outOfBounds(index);
        }
    }
    
    void rangeCheckForAdd(int index) const {
        if (index < 0 || index > m_size) {
            outOfBounds(index);
        }
    }
    
  
};

// 重载 <<
template <class T>
ostream& operator<<(ostream& out, const SingleLinkedList<T>& list) {
    list.output(out);
    return out;
}
#endif /* SingleLinkedList_hpp */

测试

int main(int argc, const char * argv[]) {
    
    SingleLinkedList<int> list;
    list.add(10).add(12).add(15).add(18).add(22);
    cout << "Create List is: " << list << endl;

    int element;
    list.remove(3, element);
    cout << "Delete elment is: " << element << endl;
    cout << "after delete size=" << list.size() << endl;
    
    list.get(2, element);
    cout << "Get index 2 value is: " << element << endl;
    list.set(3, 333);
    cout << "List is: " << list << endl;
    list.clear();
    cout << "after clear() size=" << list.size() << endl;
    cout << "after clear() List is: " << list << endl;
    return 0;
}

// 打印结果
Create List is: 10, 12, 15, 18, 22, 
Delete elment is: 18
after delete size=4
Get index 2 value is: 15
List is: 10, 12, 15, 333, 
after clear() size=0
after clear() List is: 

相关文章

  • C++ 单链表(带虚拟头节点)

    为了操作方便,统一所有节点的处理逻辑,可以在最前面增加一个没有实际含义的 虚拟头结点 ,这个虚拟头结点不存储有效数...

  • C++ 单链表(不带虚拟头节点)

  • 数据结构——链表

    本文所讲的链表是单链表,链表采用无头链表 科普下:一般链表可以分为有头节点的链表与无头节点的链表 有头节点的链表:...

  • 4.单链表逆序

    题目:给定一个带附加头节点的单链表,设first为其头指针,节点的结构为(data,link),data为数据域,...

  • [数据结构]第二章线性表(3)——单链表

    单链表 什么是单链表? 单链表的定义 别名 注释:或者可以理解为指向头节点的指针既可以表示整个单链表也可以表示头节...

  • 链表-有序单链表合并

    先简单写一下单链表的几个点单链表有带表头节点和不带表头节点两种1)带表头节点 2)不带表头节点 往往使用带表头的会...

  • 反转链表

    给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

  • 反转单链表Java实现

    问题描述 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。 解题思路 为了实现反转单链表,...

  • 单链表逆置算法详解

    思路: 首先创建一个单链表,返回一个头节点的指针( head 该头节点不为 NULL,其次进行单链表的逆置设置。具...

  • 链表相交

    题目: 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表...

网友评论

      本文标题:C++ 单链表(带虚拟头节点)

      本文链接:https://www.haomeiwen.com/subject/pcfnqrtx.html