zoukankan      html  css  js  c++  java
  • 单链表的c++实现

    链表同样是一种线性表,但只是逻辑上的线性,地址空间并不连续,而是靠指针将各个节点连接起来,就像锁链一样,一环连一环。所以,需要定义一个节点类,用来存储数据和指向下一个节点的指针。为了简单,只定义了两个公有的成员变量。(双向链表则需定义两个指针,分别指向前驱和后继)

     1 #ifndef Node_hpp
     2 #define Node_hpp
     3 
     4 class Node
     5 {
     6 public:
     7     int Data;
     8     Node *NEXT;
     9 };
    10 
    11 #endif /* Node_hpp */

    链表应该具有线性表的通用功能,所以定义函数为

     1 #ifndef LinkList_hpp
     2 #define LinkList_hpp
     3 #include "Node.hpp"
     4 
     5 class LinkList
     6 {
     7 public:
     8     LinkList();                     //建立链表(头节点)
     9     ~LinkList();                    //销毁链表
    10     void ClearList();               //清空链表
    11     bool ListEmpty();               //链表判空
    12     int ListLength();               //链表长度
    13     bool GetElem(int i,Node *pNode);//目标节点内容
    14     int LocateElem(Node *pNode);    //目标节点位置
    15     bool PriorElem(Node *pCurrentNode,Node *pPreNode);  //目标节点前驱‘
    16     bool NextElem(Node *pCurrentNode,Node *pNextNode);  //目标节点后继
    17     bool ListInsert(int i,Node *pNode);                 //插入节点
    18     bool ListDelete(int i,Node *pNode);                 //删除节点
    19     bool ListInsertHead(Node *pNode);                   //在链表开始处插入
    20     bool ListInsertTail(Node *pNode);                   //在链表尾部插入
    21     void ListTraverse();                                //遍历链表
    22 private:
    23     Node *_pList;
    24     int _iListLen;
    25 };
    26 
    27 #endif /* LinkList_hpp */

    在建立链表的时候,并不需要规定链表的容量,因为有需要加入的内容,只需new一个节点类插入进去就可以了。

    所以,构造函数只定义了一个头节点

    1 LinkList::LinkList()
    2 {
    3     _pList=new Node;
    4     _pList->Data=0;
    5     _pList->NEXT=NULL;
    6     _iListLen=0;
    7 }

    删除链表需要依次delete所有节点,而清空链表同样要delete掉除头节点外所有节点。通过while循环,只要当前节点的NEXT指向不为空,就像下一个节点移动,并删除当前节点。最后,不忘将当前节点置NULL。

     1 void LinkList::ClearList()
     2 {
     3     Node *currentNode=_pList->NEXT;
     4     while(currentNode->NEXT!=NULL)
     5     {
     6         Node *temp=currentNode->NEXT;
     7         delete currentNode;
     8         currentNode=temp;
     9     }
    10     currentNode=NULL;
    11     _iListLen=0;
    12 }

    销毁链表时,只需调用清空函数,最后销毁头节点

    1 LinkList::~LinkList()
    2 {
    3     ClearList();
    4     delete _pList;
    5     _pList=NULL;
    6 }

    根据头节点的成员变量_iListLen,可以很容易得到判空函数和长度函数。因为链表没有满的状态,所以不需要定义判满

     1 bool LinkList::ListEmpty()
     2 {
     3     if(_iListLen==0)
     4     {
     5         return true;
     6     }
     7     else
     8     {
     9         return false;
    10     }
    11 }
    12 
    13 int LinkList::ListLength()
    14 {
    15     return _iListLen;
    16 }

    求目标节点数据和位置有相似的地方,都需要依次遍历,不同的是求数据的时候要先判断给的位置是否在规定范围内,再根据给的位置进行循环,而求位置则是直接从头遍历至尾,每次循环都要对比当前节点数据和目标节点数据是否相同(如果遍历至尾都没有相同的,则返回-1)

     1 bool LinkList::GetElem(int i,Node *pNode)
     2 {
     3     if(i<0||i>=_iListLen)
     4     {
     5         return false;
     6     }
     7     else
     8     {
     9         Node *currentNodt=_pList;
    10         for(int k=0;k<=i;k++)
    11         {
    12             currentNodt=currentNodt->NEXT;
    13         }
    14         pNode->Data=currentNodt->Data;
    15         return true;
    16     }
    17 }
    18 
    19 int LinkList::LocateElem(Node *pNode)
    20 {
    21     Node *currentNode=_pList;
    22     int k=0;
    23     while(currentNode->NEXT!=NULL)
    24     {
    25         currentNode=currentNode->NEXT;
    26         if(currentNode->Data==pNode->Data)
    27         {
    28             return k;
    29         }
    30         k++;
    31     }
    32     return -1;
    33 }

    求前驱和后继的方式相似,定义两个相邻节点,遍历的同时移动,并与目标节点数据进行对比,如果求前驱,则用后面的节点对比,如果求后继,则用前面的节点对比

     1 bool LinkList::PriorElem(Node *pCurrentNode,Node *pPreNode)
     2 {
     3     Node *currentNode=_pList;
     4     Node *currentNodeBefore=NULL;
     5     while(currentNode->NEXT!=NULL)
     6     {
     7         currentNodeBefore=currentNode;
     8         currentNode=currentNode->NEXT;
     9         if(currentNode->Data==pCurrentNode->Data)
    10         {
    11             pPreNode->Data=currentNodeBefore->Data;
    12             return true;
    13         }
    14     }
    15     return false;
    16 }
    17 
    18 bool LinkList::NextElem(Node *pCurrentNode,Node *pNextNode)
    19 {
    20     Node *currentNode=_pList;
    21     Node *currentNodeBefore=NULL;
    22     while(currentNode->NEXT!=NULL)
    23     {
    24         currentNodeBefore=currentNode;
    25         currentNode=currentNode->NEXT;
    26         if(currentNodeBefore->Data==pCurrentNode->Data)
    27         {
    28             pNextNode->Data=currentNode->Data;
    29             return true;
    30         }
    31     }
    32     return false;
    33 }

    插入与删除与根据位置求数据类似,需要先判断位置是否合法,再进行遍历。因为除头节点外,所有节点都只能由前驱节点的指针得到,所以先赋值插入节点的后继节点,再将插入节点赋值给前驱节点的后继。删除则只需要将目标节点的后继节点赋值给前驱节点的NEXT指针。

     1 bool LinkList::ListInsert(int i,Node *pNode)
     2 {
     3     if(i<0||i>=_iListLen)
     4     {
     5         return false;
     6     }
     7     else
     8     {
     9         Node *currentNode=_pList;
    10         for(int k=0;k<i;k++)
    11         {
    12             currentNode=currentNode->NEXT;
    13         }
    14         Node *newNode=new Node;
    15         newNode->Data=pNode->Data;
    16         newNode->NEXT=currentNode->NEXT;
    17         currentNode->NEXT=newNode;
    18         _iListLen++;
    19         return true;
    20     }
    21 }
    22 
    23 bool LinkList::ListDelete(int i,Node *pNode)
    24 {
    25     if(ListEmpty())
    26     {
    27         return false;
    28     }
    29     else
    30     {
    31         if(i<0||i>=_iListLen)
    32         {
    33             return false;
    34         }
    35         else
    36         {
    37             Node *currentNode=_pList;
    38             Node *currentNodeBefore=NULL;
    39             for(int k=0;k<=i;k++)
    40             {
    41                 currentNodeBefore=currentNode;
    42                 currentNode=currentNode->NEXT;
    43             }
    44             currentNodeBefore->NEXT=currentNode->NEXT;
    45             pNode->Data=currentNode->Data;
    46             _iListLen--;
    47             
    48             delete currentNode;
    49             currentNode=NULL;
    50             return false;
    51         }
    52     }
    53 }

    在链表的头尾插入则是插入的特殊情况,一个是不需遍历,一个是需遍历所有

     1 bool LinkList::ListInsertHead(Node *pNode)
     2 {
     3     Node *temp=_pList->NEXT;
     4     Node *newNode=new Node;
     5     if(newNode==NULL)
     6     {
     7         return false;
     8     }
     9     else
    10     {
    11         newNode->Data=pNode->Data;
    12         newNode->NEXT=temp;
    13         _pList->NEXT=newNode;
    14         _iListLen++;
    15         return true;
    16     }
    17 }
    18 
    19 bool LinkList::ListInsertTail(Node *pNode)
    20 {
    21     Node *currentNode=_pList;
    22     while(currentNode->NEXT!=NULL)
    23     {
    24         currentNode=currentNode->NEXT;
    25     }
    26     Node *newNode=new Node;
    27     if(newNode==NULL)
    28     {
    29         return false;
    30     }
    31     else
    32     {
    33         newNode->Data=pNode->Data;
    34         newNode->NEXT=NULL;
    35         currentNode->NEXT=newNode;
    36    
    37         _iListLen++;
    38         return true;
    39     }
    40 }

    遍历链表结尾

     1 void LinkList::ListTraverse()
     2 {
     3     using namespace std;
     4     
     5     cout<<endl;
     6     Node *currentNode=_pList;
     7     while(currentNode->NEXT!=NULL)
     8     {
     9         currentNode=currentNode->NEXT;
    10         cout<<currentNode->Data<<endl;
    11     }
    12     cout<<endl;
    13 }

    最后,因为涉及到好多循环,不可避免的有许多循环次数的问题,如果分析不清楚,可以通过在主函数中调用检验

     1 #include <iostream>
     2 #include "LinkList.hpp"
     3 
     4 int main(int argc, const char * argv[]) {
     5     // insert code here...
     6     using namespace std;
     7     
     8     LinkList *p=new LinkList;
     9     
    10     Node *c=new Node;
    11     
    12     c->Data=3;
    13     p->ListInsertHead(c);
    14     c->Data=2;
    15     p->ListInsertHead(c);
    16     c->Data=5;
    17     p->ListInsertTail(c);
    18     c->Data=4;
    19     p->ListInsert(2,c);
    20     
    21     p->ListTraverse();
    22     
    23     Node *d=new Node;
    24     
    25     p->GetElem(3, d);
    26     cout<<d->Data<<endl;
    27     p->PriorElem(c, d);
    28     cout<<c->Data<<endl;
    29     cout<<d->Data<<endl;
    30     p->NextElem(c, d);
    31     cout<<c->Data<<endl;
    32     cout<<d->Data<<endl;
    33     p->ListDelete(2, d);
    34     cout<<d->Data<<endl;
    35     
    36     p->ListTraverse();
    37     
    38     delete d;
    39     d=NULL;
    40     
    41     delete c;
    42     c=NULL;
    43     
    44     delete p;
    45     p=NULL;
    46     
    47     return 0;
    48 }
    49 #include <iostream>
    50 #include "LinkList.hpp"
    51 
    52 int main(int argc, const char * argv[]) {
    53     // insert code here...
    54     using namespace std;
    55     
    56     LinkList *p=new LinkList;
    57     
    58     Node *c=new Node;
    59     
    60     c->Data=3;
    61     p->ListInsertHead(c);
    62     c->Data=2;
    63     p->ListInsertHead(c);
    64     c->Data=5;
    65     p->ListInsertTail(c);
    66     c->Data=4;
    67     p->ListInsert(2,c);
    68     
    69     p->ListTraverse();
    70     
    71     Node *d=new Node;
    72     
    73     p->GetElem(3, d);
    74     cout<<d->Data<<endl;
    75     p->PriorElem(c, d);
    76     cout<<c->Data<<endl;
    77     cout<<d->Data<<endl;
    78     p->NextElem(c, d);
    79     cout<<c->Data<<endl;
    80     cout<<d->Data<<endl;
    81     p->ListDelete(2, d);
    82     cout<<d->Data<<endl;
    83     
    84     p->ListTraverse();
    85     
    86     delete d;
    87     d=NULL;
    88     
    89     delete c;
    90     c=NULL;
    91     
    92     delete p;
    93     p=NULL;
    94     
    95     return 0;
    96 }

    数据结构线性表基础部分告一段落

  • 相关阅读:
    vue2.0之render函数
    vuex学习笔记
    js小知识点
    vue2.0路由进阶
    vue2.0使用slot插槽分发内容
    js事件机制
    firefox在引入vue.js后不支持e=e||window.event的解决办法
    qs.js使用方法
    除了使用URLSearchParams处理axios发送的数据,但是兼容性不好,其他的兼容方法
    定义设置滚动条
  • 原文地址:https://www.cnblogs.com/Bird-of-Paradise/p/6367319.html
Copyright © 2011-2022 走看看