C++中单向链表类模板和iterator迭代器类的示例分析(C++,开发技术)

时间:2024-05-01 14:51:57 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

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

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

    1.链表介绍

    常见的线性链表分为三种

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

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

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

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

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

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

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

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

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

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

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

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

    拥有头结点的好处:

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

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

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

    如下图所示:

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

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

    Node*pre=getNode(i-1);//获取上个节点Node*node=newNode();//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中deletecurrent;//delete空闲的节点m_length-=1;

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

    代码如下所示:

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

    2.实现单链表

    需要实现的函数:

    intlength() : 获取链表数据长度

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

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

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

    boolremove(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<<"fromindex0find2:"<<list.indexOf(2)<<endl;//returns1cout<<"fromindex1find2:"<<list.indexOf(2,1)<<endl;//returns1cout<<"fromindex2find2:"<<list.indexOf(2,2)<<endl;//从索引号2开始查找,returns3cout<<"fromindex0find3:"<<list.indexOf(3)<<endl;//returns-1

    打印效果如下所示:

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

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

    #ifndefSingleLinkedLIST_H#defineSingleLinkedLIST_H#include"throw.h"//throw.h里面定义了一个ThrowException抛异常的宏,如下所示://#include<iostream>//usingnamespacestd;//#defineThrowException(errMsg){cout<<__FILE__<<"LINE"<<__LINE__<<":"<<errMsg<<endl;(throwerrMsg);}/*链表节点类模板*/template<typenameT>structSingleLinkedNode{inlineSingleLinkedNode(){}inlineSingleLinkedNode(constT&arg):value(arg){}SingleLinkedNode*next;//后驱节点Tvalue;//节点值};/*单链表类模板*/template<classT>classSingleLinkedList{protected:typedefSingleLinkedNode<T>Node;Nodem_header;//头节点intm_length;public:SingleLinkedList(){m_header.next=nullptr;m_length=0;}~SingleLinkedList(){clear();}voidappend(constT&value){insert(m_length,value);}voidprepend(constT&value){insert(0,value);}intlength(){returnm_length;}Node*begin(){returnm_header.next;}staticboolrangeValid(inti,intlen){return((i>=0)&&(i<len));}/*获取i位置处的节点*/Node*getNode(inti){Node*ret=&m_header;while((i--)>-1){//由于有头节点所以,i为0时,其实ret=m_header->nret=ret->next;}returnret;}/*插入一个新的节点*/boolinsert(inti,constT&value){if(!((i>=0)&&(i<=m_length))){ThrowException("Invalidparameteritogetvalue...");returnfalse;}Node*pre=getNode(i-1);Node*node=newNode(value);//new一个新节点node->next=pre->next;//将新节点的next链接到下个节点pre->next=node;//将前个节点的next链接到创建的新节点m_length+=1;returntrue;}/*删除一个节点*/boolremove(inti){if(!rangeValid(i,m_length)){ThrowException("Invalidparameteritogetvalue...");returnfalse;}Node*pre=getNode(i-1);Node*current=pre->next; //获取要删除的节点pre->next=current->next;//将当前节点的下个节点链接到前一个的next中deletecurrent;//delete空闲的节点m_length-=1;returntrue;}/*获取节点数据*/Tget(inti){Tret;if(!rangeValid(i,m_length)){ThrowException("Invalidparameteritogetvalue...");}else{ret=getNode(i)->value;}returnret;}/*设置节点*/boolset(inti,constT&value){if(!rangeValid(i,m_length)){ThrowException("Invalidparameteritogetvalue...");returnfalse;}getNode(i)->value=value;returntrue;}voidclear(){while(m_header.next){Node*node=m_header.next;m_header.next=node->next;deletenode;}m_length=0;}SingleLinkedList<T>&operator<<(constT&value){append(value);return*this;}/*在链表中向前查找value所在的索引号.默认从from索引号0(表头)开始.如果未找到则返回-1.*/intindexOf(constT&value,intfrom=0){intret=0;Node*node=m_header.next;while(node){if(ret>=from&&node->value==value){returnret;}node=node->next;ret+=1;}return-1;}};/*单链表迭代器类模板*/template<classT>classSingleLinkedListIterator{typedefSingleLinkedNode<T>Node;SingleLinkedList<T>*list;Node*m_current;//当前指标public:explicitSingleLinkedListIterator(SingleLinkedList<T>&l):list(&l){m_current=l.begin();}voidtoBegin(){m_current=list->begin();}boolhasNext(){return(m_current);}T&next(){Node*ret=m_current;m_current=m_current->next;returnret->value;}T&value(){if(m_current==nullptr){ThrowException("Currentvalueisempty...");}returnm_current->value;}T&move(inti){if(!list->rangeValid(i,list->length())){ThrowException("Invalidparameteritogetvalue...");}m_current=list->getNode(i);returnvalue();}};#endif//SingleLinkedLIST_H

    测试代码如下所示:

    SingleLinkedList<int>list;for(inti=0;i<5;i++)list.append(i);for(inti=0;i<5;i++)list<<i+5;cout<<"print:"<<endl;cout<<"list.length:"<<list.length()<<endl;for(inti=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(inti=0;i<list.length();i++){cout<<""<<list.get(i)<<"";}cout<<endl;

    运行打印:

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

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

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

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

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

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

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

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

    • boolhasNext(): 是否有下个节点

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

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

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

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

    迭代器类实现如下所示:

    /*单链表迭代器类模板*/template<classT>classSingleLinkedListIterator{typedefSingleLinkedNode<T>Node;SingleLinkedList<T>*list;Node*m_current;//当前指标public:explicitSingleLinkedListIterator(SingleLinkedList<T>&l):list(&l){m_current=l.begin();}voidtoBegin(){m_current=list->begin();}boolhasNext(){return(m_current);}T&next(){Node*ret=m_current;m_current=m_current->next;returnret->value;}T&value(){if(m_current==nullptr){ThrowException("Currentvalueisempty...");}returnm_current->value;}T&move(inti){if(!list->rangeValid(i,list->length())){ThrowException("Invalidparameteritogetvalue...");}m_current=list->getNode(i);returnvalue();}};

    示例代码如下所示:

    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迭代器类的示例分析

     </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
    本文:C++中单向链表类模板和iterator迭代器类的示例分析的详细内容,希望对您有所帮助,信息来源于网络。
    上一篇:SSM框架下各层的示例分析下一篇:

    9 人围观 / 0 条评论 ↓快速评论↓

    (必须)

    (必须,保密)

    阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18