温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C++中单向链表类模板和iterator迭代器类的示例分析

发布时间:2022-02-28 09:31:48 来源:亿速云 阅读:161 作者:小新 栏目:开发技术

这篇文章主要介绍了C++中单向链表类模板和iterator迭代器类的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

    链表用来构建许多其它数据结构,如堆栈,队列和他们的派生。

    对于非线性的链表,可以参见相关的其他数据结构,例如二叉树、图等。

    1.链表介绍

    常见的线性链表分为三种

    单链表: 每个结点都含有指向其后继结点的地址信息

    C++中单向链表类模板和iterator迭代器类的示例分析

    双向链表: 每个结点都有指向其前驱结点和后继结点的地址信息

    C++中单向链表类模板和iterator迭代器类的示例分析

    循环双向链表: 在双向链表的基础上,将数据结点头的前驱信息保存数据结点尾部地址,数据结点尾部的后驱信息保存数据结点头地址、

    C++中单向链表类模板和iterator迭代器类的示例分析

    链表中包含的关键词如下所示:

    • 链表头: 也就是head指针, 每次访问链表时都可以从这个头指针依次遍历链表中的每个元素

    • 头结点: 数据内容无效,指向数据结点

    • 数据结点: 存储数据元素的结点

    • 尾结点:数据内容无效,位于数据结点尾部,标志最后一个结点

    对于链表而言,链表头必须存在。而头结点和尾结点在有些链表中是不存在的,但是拥有头结点会有很大的好处

    拥有头结点的好处:

    每次插入删除时,无需判断是否为第一个结点(对于无头结点的链表,每次都要判断如果是第一个结点,需要将前驱信息设置为链表头,并且将链表头的后继信息设置为第一个结点)

    如果是双向循环链表(下章实现),我们可以通过头结点的前驱节点轻松获取到最后一个数据结点,从而实现append函数进行尾部插入结点,无需每次遍历链表至末尾再插入结点.

    1.1 单链表插入某个节点流程

    如下图所示:

    C++中单向链表类模板和iterator迭代器类的示例分析

    从头结点开始遍历,通过要插入的索引号-1找到pre指针后,代码如下所示:

    Node* pre = getNode(i-1);     // 获取上个节点
    Node* node = new Node();      // new一个新节点
    node->data = value;           // 设置data数据元素
    node->next = pre->next;       // 将新节点的next链接到下个节点
    pre->next = node;             // 将前个节点的next链接到创建的新节点
    m_length += 1;

    1.2 单链表删除某个节点流程

    如下图所示:

    C++中单向链表类模板和iterator迭代器类的示例分析

    从头结点开始遍历,通过要删除的索引号-1找到current指针的前一个结点pre后,代码如下所示:

    Node* pre = getNode(i-1);
    Node* current = pre->next;     // 获取要删除的节点
    pre->next = current->next;     // 将当前节点的下个节点链接到前一个的next中
    delete current;                // delete空闲的节点
    m_length -= 1;

    1.3 单链表清除所有节点流程

    代码如下所示:

        while(m_header.next) {
            Node* node = m_header.next;
            m_header.next = node->next;
            delete node;
        }
        m_length = 0;

    2.实现单链表

    需要实现的函数:

    int length() : 获取链表数据长度

    void clear() : 清空链表所有数据

    Node* getNode(int i): 获取i处的节点

    bool insert(int i, const T& value) : 在索引号i处插入一个新的数据

    bool remove(int i) : 删除链表中索引号i所在的数据

    T get(int i): 获取i处的数据

    bool set(int i, const T& value): 设置i处的数据

    void append(const T &value) :在链表尾部追加一个新的数据

    void prepend(const T &value) : 在链表头部插入一个新的数据

    void clear() : 清空链表内容

    LinkedList& operator << (const T& value):  重写<<操作符,方便尾部追加数据

    int indexOf(const T &value, int from =0) : 在链表中向前查找value所在的索引号.默认从from索引号0(表头)开始.如果未找到则返回-1.

    2.1indexOf()函数示例如下所示:

    LinkedList<int> list;
    list << 1 << 2 << 4 << 2 << 6;
    cout<<"from index0 find 2 :"<<list.indexOf(2)<<endl;    //returns 1
    cout<<"from index1 find 2 :"<<list.indexOf(2, 1)<<endl; //returns 1
    cout<<"from index2 find 2 :"<<list.indexOf(2, 2)<<endl; //从索引号2开始查找,returns 3
    cout<<"from index0 find 3 :"<<list.indexOf(3)<<endl;    //returns -1

    打印效果如下所示:

    C++中单向链表类模板和iterator迭代器类的示例分析

    本章SingleLinkedList.h的整个代码实现如下所示(包含迭代器类):

    #ifndef SingleLinkedLIST_H
    #define SingleLinkedLIST_H
    #include "throw.h"
    // throw.h里面定义了一个ThrowException抛异常的宏,如下所示:
    //#include <iostream>
    //using namespace std;
    //#define ThrowException(errMsg)  {cout<<__FILE__<<" LINE"<<__LINE__<<": "<<errMsg<<endl; (throw errMsg);}
    /*链表节点类模板*/
    template <typename T>
    struct SingleLinkedNode
    {
        inline SingleLinkedNode(){ }
        inline SingleLinkedNode(const T &arg): value(arg) { }
        SingleLinkedNode *next;        // 后驱节点
        T value;                 // 节点值
    };
    /*单链表类模板*/
    template <class T>
    class SingleLinkedList
    {
    protected:
        typedef SingleLinkedNode<T> Node;
        Node m_header;          // 头节点
        int m_length;
    public:
        SingleLinkedList() { m_header.next = nullptr; m_length = 0; }
        ~SingleLinkedList() { clear(); }
        void append(const T &value) { insert(m_length, value);}
        void prepend(const T &value) {insert(0, value);}
        int length()  {return m_length;}
        Node* begin() {return m_header.next;}
        static bool rangeValid(int i,int len)  {return ((i>=0) && (i<len));}
        /*获取i位置处的节点*/
        Node* getNode(int i)
        {
            Node* ret = &m_header;
            while((i--)>-1) {       // 由于有头节点所以,i为0时,其实ret = m_header->n
                ret = ret->next;
            }
            return ret;
        }
        /*插入一个新的节点*/
        bool insert(int i, const T& value)
        {
            if (!((i>=0) && (i<=m_length))) {
                ThrowException("Invalid parameter i to get value ...");
                return false;
            }
            Node* pre = getNode(i-1);
            Node* node = new Node(value);    // new一个新节点
            node->next = pre->next;          // 将新节点的next链接到下个节点
            pre->next = node;                // 将前个节点的next链接到创建的新节点
            m_length +=1;
            return true;
        }
        /*删除一个节点*/
        bool remove(int i)
        {
            if (!rangeValid(i, m_length)) {
                ThrowException("Invalid parameter i to get value ...");
                return false;
            }
            Node* pre = getNode(i-1);
            Node* current = pre->next;		 // 获取要删除的节点
            pre->next = current->next;       // 将当前节点的下个节点链接到前一个的next中
            delete current;                  // delete空闲的节点
            m_length -=1;
            return true;
        }
        /*获取节点数据*/
        T get(int i)
        {
            T ret;
            if (!rangeValid(i, m_length)) {
                ThrowException("Invalid parameter i to get value ...");
            } else {
                ret = getNode(i)->value;
            }
            return ret;
        }
        /*设置节点*/
        bool set(int i, const T& value)
        {
            if (!rangeValid(i, m_length)) {
                ThrowException("Invalid parameter i to get value ...");
                return false;
            }
            getNode(i)->value = value;
            return true;
        }
        void clear()
        {
            while(m_header.next) {
                Node* node = m_header.next;
                m_header.next = node->next;
                delete node;
            }
            m_length = 0;
        }
        SingleLinkedList<T>& operator << (const T& value)
        {
            append(value);
            return *this;
        }
        /*在链表中向前查找value所在的索引号.默认从from索引号0(表头)开始.如果未找到则返回-1.*/
        int indexOf(const T &value, int from =0)
        {
            int ret = 0;
            Node* node = m_header.next;
            while(node) {
               if (ret >= from && node->value == value) {
                   return ret;
               }
               node = node->next;
               ret+=1;
            }
            return -1;
        }
    };
    /*单链表迭代器类模板*/
    template <class T>
    class SingleLinkedListIterator
    {
        typedef SingleLinkedNode<T> Node;
        SingleLinkedList<T> *list;
        Node *m_current;     // 当前指标
    public:
        explicit SingleLinkedListIterator(SingleLinkedList<T> &l):list(&l) { m_current = l.begin(); }
        void toBegin() { m_current = list->begin(); }
        bool hasNext()  { return (m_current); }
        T& next() { Node *ret = m_current;  m_current = m_current->next; return ret->value; }
        T& value()
        {
            if (m_current == nullptr) {
                ThrowException(" Current value is empty ...");
            }
            return m_current->value;
        }
        T& move(int i)  {
            if (!list->rangeValid(i, list->length())) {
                ThrowException("Invalid parameter i to get value ...");
            }
            m_current = list->getNode(i);
            return value();
        }
    };
    #endif // SingleLinkedLIST_H

    测试代码如下所示:

        SingleLinkedList<int> list;
        for(int i = 0; i< 5; i++)
          list.append(i);
        for(int i = 0; i< 5; i++)
          list<<i+5;
        cout<<"print:"<<endl;
        cout<<"list.length:"<<list.length()<<endl;
        for(int i = 0; i< list.length(); i++){
            cout<<" "<<list.get(i)<<" ";
        }
        cout<<endl;
        // 修改链表数据
        list.set(1,100);
        list.set(2,200);
        list.remove(3);
        list.insert(5,500);
        cout<<"changed:"<<endl;
        cout<<"list.length:"<<list.length()<<endl;
        for(int i = 0; i< list.length(); i++){
            cout<<" "<<list.get(i)<<" ";
        }
        cout<<endl;

    运行打印:

    C++中单向链表类模板和iterator迭代器类的示例分析

    3.实现一个迭代器来优化链表遍历

    迭代器(iterator)有时又称光标(cursor)是程序设计的软件设计模式,可在容器对象(container,例如链表或数组)上遍访的接口,设计人员无需关心容器对象的内存分配的实现细节。

    3.1 为什么要实现一个迭代器?

    比如我们刚刚写的遍历链表代码:

    for(int i = 0; i< list.length(); i++){        // 时间复杂度为O(n)
            cout<<" "<<list.get(i)<<" ";         // get函数的时间复杂度为O(n)
    }

    每次for循环调用链表的get时,都会重复去遍历链表,所以遍历一个链表需要的时间复杂度为O(n^2),所以我们需要实现迭代器来优化链表遍历

    迭代器需要实现以下几个函数:

    • bool hasNext(): 是否有下个节点

    • T &next(): 移动光标到下一个节点,并返回之前的值

    • T &value(): 获取当前光标的节点数据

    • void toBegin(): 将迭代器的光标定位到开头位置

    • T& move(int i): 将迭代器当前光标定位到i位置处,并返回当前位置的值

    迭代器类实现如下所示:

    /*单链表迭代器类模板*/
    template <class T>
    class SingleLinkedListIterator
    {
        typedef SingleLinkedNode<T> Node;
        SingleLinkedList<T> *list;
        Node *m_current;     // 当前指标
    public:
        explicit SingleLinkedListIterator(SingleLinkedList<T> &l):list(&l) { m_current = l.begin(); }
        void toBegin() { m_current = list->begin(); }
        bool hasNext()  { return (m_current); }
        T& next() { Node *ret = m_current;  m_current = m_current->next; return ret->value; }
        T& value()
        {
            if (m_current == nullptr) {
                ThrowException(" Current value is empty ...");
            }
            return m_current->value;
        }
        T& move(int i)  {
            if (!list->rangeValid(i, list->length())) {
                ThrowException("Invalid parameter i to get value ...");
            }
            m_current = list->getNode(i);
            return value();
        }
    };

    示例代码如下所示:

        SingleLinkedList<int> list;
        list<<1<<4<<5<<6<<8;
        SingleLinkedListIterator<int> it(list);
        cout<<"print:"<<endl;
        cout<<"list.length:"<<list.length()<<endl;
        while (it.hasNext())        // 通过迭代器让时间复杂度为O(n)
            cout<<it.next()<<endl;
        cout<<endl;
        cout<<"moved:"<<endl;
        it.move(2);
        while (it.hasNext())        // 通过迭代器让时间复杂度为O(n)
            cout<<it.next()<<endl;

    打印如下所示:

    C++中单向链表类模板和iterator迭代器类的示例分析

    感谢你能够认真阅读完这篇文章,希望小编分享的“C++中单向链表类模板和iterator迭代器类的示例分析”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    c++
    AI