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