鏈表同樣是一種線性表,但只是邏輯上的線性,地址空間並不連續,而是靠指針將各個節點連接起來,就像鎖鏈一樣,一環連一環。所以,需要定義一個節點類,用來存儲數據和指向下一個節點的指針。為了簡單,只定義了兩個公有的成員變量。(雙向鏈表則需定義兩個指針,分別指向前驅和后繼)
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 }
數據結構線性表基礎部分告一段落