单向链表的C++实现

本文介绍单向链表的基础概念与C++实现方法,包括链表节点定义、链表类构造与成员函数实现等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单向链表的C++实现

分类: Data Structures 208人阅读 评论(0) 收藏 举报


 

        链表作为一种基础的数据结构,在计算机科学中广泛应用。特别是在不支持连续存储的空间中,以及实现灵活的内存动态管理中,都能起到重要作用。链表在首尾端及附近的操作普遍优于一般数组array,而且支持在链表内部找到指定位置并插入和删除元素,没必要像数组一样移动一大串元素,而只是在内部对所改变的结点链接进行固定的几个改变即可;但在链表中搜寻元素要遍历链表需O(n)的时间,而不像数组中用O(1)那么快,而且链表在中间的操作会比首尾端慢上不少。

        要组成一整条单向链表,就需要定义好链表上的各个结点,并且将单向链表上各结点串在一起:

  1. #ifndef SIMPLENODE_HPP  
  2. #define SIMPLENODE_HPP  
  3. template<typename T>         //定义一个模板T  写单链表   
  4. class SingleNode  
  5. {  
  6. public:  
  7.     T element;       
  8.     SingleNode* next;  
  9.     SingleNode(const T& theElement,SingleNode* nextone=NULL)  
  10.                :element(theElement),next(nextone) {}     
  11. };  
  12. #endif  

        这个单链表形成后,结构看起来就像这样的:
       

        在组成一个单向链表后,还需要补充一些功能,以便后续实现:

        1.定义这个单向链表类的构造函数及析构函数

        2.清空链表内所有元素

        3.给出元素位置再返回对应结点

        4.返回链表内部元素个数

        5.判断这个链表是否为空

        6.返回链表首尾的元素值

        7.查找元素是否在此链表内,如果在则返回所在位置

        8.从首端到尾端输出链表上的各元素

        9.对链表插入元素以及删除元素

 

        与此同时,顺理成章地给出这些功能的接口们,他们组成了这个单链表类的参考框架:

  1. #ifndef SINGLELINKLIST_HPP  
  2. #define SINGLELINKLIST_HPP  
  3. #include<iostream>  
  4. #include"simplenode.hpp"  
  5. //这里默认位置pos的开始是从1开始,而不是从0开始   
  6. template<class T>  
  7. class SingleLinkList          //单链表类的定义  
  8. {  
  9. private:  
  10.     SingleNode<T>* head;           //链表头指针  
  11.     SingleNode<T>* tail;           //链表尾指针  
  12.     int size;             //元素个数  
  13.     SingleNode<T>* GetPointAt(int pos)   
  14.     {...}  //给出元素位置再返回其对应结点  
  15. public:  
  16.     SingleLinkList():head(),tail(),size(0) {}  
  17.     ~SingleLinkList() {Clear();}  
  18.     void Clear()  
  19.     {...}  
  20.     int Size() {...}        //返回元素个数  
  21.     bool isempty()  {...}  //返回链表是否为空   
  22.     //-----------------------------------------------------  
  23.     //这里添加元素  
  24.     //------在尾部添加元素  
  25.     void AddBack(T val)  
  26.     {...}   
  27.     //------在指定位置插入元素  
  28.     bool AddAt(T val,int pos)  
  29.     {  
  30.         SingleNode<T>* pNode=NULL;  
  31.         if (pos<=0 || pos>size)   //插入位置越界  
  32.         {...}  
  33.         if (pos==size)       //在尾部插入元素       
  34.             AddBack(val);  
  35.         else if (pos==1)      //在头部插入元素   
  36.         {...}  
  37.         else  
  38.         {...}  
  39.         size++;  
  40.         return true;  
  41.     }   
  42.     //-----------------------------------------------  
  43.     //这里删除元素   
  44.     bool RemoveBack()         //删除尾部元素  
  45.     {  
  46.         return RemoveAt(size);      
  47.     }   
  48.     bool RemoveAt(int pos)      //删除指定位置元素  
  49.     {  
  50.         SingleNode<T>* pNode=NULL;  
  51.         if (isempty())  
  52.         {...}  
  53.         if (pos<=0 || pos>size)  
  54.         {...}  
  55.         if (size==1)       //只有1个元素时相当于清空链表  
  56.         {  
  57.             Clear();  
  58.         }   
  59.         if (pos==1)        //并且size!=1, 删除头部元素时   
  60.         {...}   
  61.         else if (pos==size)  //size!=1,删除尾部元素时  
  62.         {...}  
  63.         else  
  64.         {...}  
  65.         size--;  
  66.         return true;  
  67.     }   
  68.     //---------------------------------------  
  69.     T GetHeadVal()      //返回首端元素  
  70.     {...}  
  71.     T GetTailVal()      //返回尾端元素  
  72.     {...}  
  73.     int Find(T val)      //查找元素  
  74.     {...}  
  75.     void ShowAllVal()    //从头到尾输出链表上的元素   
  76.     {...}   
  77. };   
  78. #endif  


        现在,就具体看看这个单向链表怎么实现的:

        1.构造函数和析构函数:

  1. SingleLinkList():head(),tail(),size(0) {}  
  2. ~SingleLinkList() {Clear();}  

        创建了一个新的空链表,首端和尾端均指向NULL,内部元素个数为0;由于在加入元素时用了new,所以要手动地delete,Clear()中进行,析构函数直接调用Clear()即可。

        2.清空链表内所有元素:

  1. void Clear()  
  2. {  
  3.     //从链表头到链表尾的方式逐个删除   
  4.     const int nums=Size();  
  5.     if (!isempty())  
  6.     {  
  7.         for (int k=1;k<=nums;++k)  
  8.         {  
  9.             SingleNode<T>* temp=head->next;  
  10.             delete head;  
  11.             head=temp;  
  12.             size--;  
  13.         }  
  14.     }  
  15.     //如果链表本来就为空,就没必要再进for循环了   
  16. }  

 

        3.给出元素位置再返回对应结点:

  1. SingleNode<T>* GetPointAt(int pos)   
  2. {  
  3.     SingleNode<T>* pNode=NULL;  
  4.     if (pos<=0 || pos>size)  
  5.         std::cout<<"out of range."<<std::endl;   //链表当前位置越界,异常  
  6.     else  
  7.     {  
  8.         pNode=head;            //当前位置满足条件,则一开始在链表头            
  9.         for (int i=1;i<=pos-1;++i)     
  10.             pNode=pNode->next;  
  11.     }   
  12.     return pNode;  
  13. }  

        要注意的是,遍历元素位置时避免pNode所获得的值越界,队首元素进不了for循环,改变不了pNode;队尾元素如果设置成i=1;i<=pos;++i,pNode会读取队尾的下一个值,那个值越界。最后就会返回一个未定义的元素。

        4.返回首尾端元素、查找元素、输出所有元素、返回元素个数、判断链表是否为空:

  1. T GetHeadVal()  
  2. {  
  3.     if (isempty())  
  4.     {  
  5.         std::cout<<"the link list is empty"<<std::endl;  
  6.         return NULL;     
  7.     }  
  8.     return head->element;  
  9. }  
  10. T GetTailVal()  
  11. {  
  12.     if (isempty())  
  13.     {  
  14.         std::cout<<"the link list is empty"<<std::endl;  
  15.         return NULL;     
  16.     }  
  17.     return tail->element;  
  18. }  
  1. int Find(T val)      //查找元素  
  2. {  
  3.     int pos=1;          //从起始位置1号位开始  
  4.     SingleNode<T>* findNode=head;  
  5.     while (findNode!=NULL)  
  6.     {  
  7.         if (findNode->element==val)  
  8.             return pos;  
  9.         findNode=findNode->next;  
  10.         pos++;  
  11.     }  
  12.     std::cout<<"we can't find it,return -1"<<std::endl;  
  13.     return -1;  
  14. }  
  1. void ShowAllVal()    //从头到尾输出链表上的元素   
  2. {  
  3.     SingleNode<T>* findNode=head;  
  4.     while (findNode!=NULL)  
  5.     {  
  6.         std::cout<<findNode->element<<" ";  
  7.         findNode=findNode->next;  
  8.     }  
  9.     std::cout<<std::endl;   
  10. }   
  1. int Size() {return size;}        //返回元素个数  
  2. bool isempty()  {return size==0?true:false; }  //返回链表是否为空   

 

        5.链表中加入元素:

        这里分四种情况讨论:

                ①插入元素位置越界                               

                ②在尾部插入元素                                   

                ③在头部插入元素                           

                ④在其他位置插入元素                   

        若插入元素成功,记得元素个数+1。这里用图示例非空链表首部插入元素,其他情况读者们可以自己试着画图:

 

        代码如下:

  1. //------在尾部添加元素  
  2. void AddBack(T val)  
  3. {  
  4.     SingleNode<T>* pNode=new SingleNode<T>(val);  
  5.     if (isempty())   //链表为空时   
  6.     {  
  7.         head=pNode; tail=pNode;   
  8.     }  
  9.     else  
  10.     {  
  11.         tail->next=pNode;        
  12.         tail=pNode;  
  13.     }  
  14.     size++;  
  15. }   
  16. //------在指定位置插入元素  
  17. bool AddAt(T val,int pos)  
  18. {  
  19.     SingleNode<T>* pNode=NULL;  
  20.     if (pos<=0 || pos>size)  
  21.     {  
  22.         std::cout<<"out of range."<<std::endl;  
  23.         return false;  
  24.     }  
  25.     if (pos==size)       //在尾部插入元素       
  26.         AddBack(val);  
  27.     else if (pos==1)      //在头部插入元素   
  28.     {  
  29.         pNode=new SingleNode<T>(val);  
  30.         pNode->next=head;  
  31.         head=pNode;  
  32.     }  
  33.     else  
  34.     {  
  35.         //返回插入位置前面一个的位置指针   
  36.         SingleNode<T>* pNode=GetPointAt(pos-1);    
  37.         SingleNode<T>* newNode=new SingleNode<T>(val);  
  38.         newNode->next=pNode->next;  
  39.         pNode->next=newNode;  
  40.     }  
  41.     size++;  
  42.     return true;  
  43. }   


 

        6.链表中删除元素:

        这里分五种情况讨论:

                ①链表为空                                

                ②删除元素位置越界                               

                ③在尾部删除元素

                ④在头部删除元素

                ⑤在其他位置删除元素

        若删除元素成功,记得元素个数-1。这里用图示例非空链表尾部删除元素,其他情况读者们可以自己试着画图:

 

        代码如下:

  1. bool RemoveBack()         //删除尾部元素  
  2. {  
  3.     return RemoveAt(size);      
  4. }   
  5. bool RemoveAt(int pos)      //删除指定位置元素  
  6. {  
  7.     SingleNode<T>* pNode=NULL;  
  8.     if (isempty())  
  9.     {  
  10.         std::cout<<"the link list is empty"<<std::endl;  
  11.         return false;  
  12.     }  
  13.     if (pos<=0 || pos>size)  
  14.     {  
  15.         std::cout<<"out of range."<<std::endl;  
  16.         return false;  
  17.     }  
  18.     if (size==1)       //只有1个元素时相当于清空链表  
  19.     {  
  20.         Clear();  
  21.     }   
  22.     if (pos==1)        //并且size!=1, 删除头部元素时   
  23.     {  
  24.         pNode=head;  
  25.         head=head->next;  
  26.         delete pNode;  
  27.     }   
  28.     else if (pos==size)  
  29.     {  
  30.         SingleNode<T>* pPreNode=GetPointAt(pos-1);  
  31.         std::cout<<"之前一个元素为"<<pPreNode->element<<std::endl;  
  32.         pNode=pPreNode->next;  
  33.         pPreNode->next=pNode->next;  
  34.         delete pNode;  
  35.         tail=pPreNode;   
  36.     }  
  37.     else  
  38.     {  
  39.         SingleNode<T>* pPreNode=GetPointAt(pos-1);  
  40.         std::cout<<"之前一个元素为"<<pPreNode->element<<std::endl;  
  41.         pNode=pPreNode->next;  
  42.         pPreNode->next=pNode->next;  
  43.         delete pNode;  
  44.     }  
  45.     size--;  
  46.     return true;  
  47. }   


 

 

         最后,将上述实现放入之前提到的框架内,就完成了最后的单向链表C++实现。

         简易的单向链表,就这么完成了。还有更加完善的单向链表,那就是STL中的<slist>了,坐等后续更新吧。

 

参考文献及链接:

1.《算法导论》2nd   Thomas H.Cormen , Charles E.Leiserson , Ronald L.Rivest , Cliford Stein 著,潘金贵、顾铁成、李成法、叶懋译
2.《数据结构与算法分析 C++描述》 第3版  Mark Allen Weiss著,张怀勇等译
3.《C++标准模板库 -自修教程及参考手册-》  Nicolai M.Josuttis著,侯捷/孟岩译

4.http://blog.youkuaiyun.com/weiwenhp/article/details/8634469

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值