链表系列二:链表的C++表示
这里以单链表为例阐述如何用C++实现表的基本表示:
1)表的结构
单链表的每一个结点都是由一个任意数据类型的元素和一个指向下一个元素的指针构成的,如下:
通过开始结点A0可以访问整个表元素,最后的一个结点的指针为NULL。
2)引入表头结点
在实现中,为了实现对各个结点的处理的一致性,引入了一个头结点,头结点是指向链表开始结点A0的结点,这样一来链表的所有结点都有了前驱结点,开始结点(链表实际结点的第一个结点)的插入和删除也就不会特别处理,对空表或非空表的处理也是一致的。引入头结点后的结构如下:
其中头结点的数据域可以不保存任何数据信息或者保存如链表长度这样的数据,头结点的指针指向链表的开始结点。
3)链表的C++表示
根据上面的结构将链表抽象为两个类:ListNode,myList。ListNode封装了一个结点的信息,包含一个任意数据类型的数据和一个指向下一个元素的指针;myList封装了一个链表的数据和行为,myList通过头结点表示的,给出了一个头结点就相当于给了一个链表,这里采用模板类以使链表可以适应各种各样的数据类型,在VS2010中具体的C++代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* ListNode.h*/ 2 #ifndef LISTNODE_H 3 #define LISTNODE_H 4 5 template <typename T> class myList; 6 7 template <typename T> 8 class ListNode 9 { 10 friend class myList<T>; 11 public: 12 ListNode() 13 { 14 data = 0; 15 next = NULL; 16 } 17 ListNode(T val) 18 { 19 data = val; 20 next = NULL; 21 } 22 private: 23 T data; 24 ListNode *next; 25 }; 26 27 #endif
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* myList.h */ 2 #ifndef MYLIST_H 3 #define MYLIST_H 4 5 #include "ListNode.h" 6 7 template <class T> 8 class myList 9 { 10 public: 11 myList(); 12 ListNode<T>* Find(T val); 13 ListNode<T>* FindPrev(T val); 14 ListNode<T>* Last(); 15 void Insert(ListNode<T>* x, T data); 16 void Delete(T data); 17 void Attach(T data); 18 void Print(); 19 20 private: 21 ListNode<T> *head; 22 }; 23 24 #endif
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* myList.cpp */ 2 #include "myList.h" 3 #include<iostream> 4 using namespace std; 5 6 template<class T> 7 myList<T>::myList() 8 { 9 head = new ListNode<T>; 10 } 11 12 template<class T> 13 ListNode<T>* myList<T>::Find(T val) 14 { 15 ListNode<T>* cur = head->next; 16 while(cur != NULL) 17 { 18 if(cur->data == val) 19 return cur; 20 cur = cur->next; 21 } 22 return NULL; 23 } 24 25 template<class T> 26 ListNode<T>* myList<T>::FindPrev(T val) 27 { 28 ListNode<T>* cur = head->next; 29 ListNode<T>* prev = head->next; 30 while(cur != NULL) 31 { 32 if(cur->data == val) 33 return prev; 34 prev = cur; 35 cur = cur->next; 36 } 37 return NULL; 38 } 39 40 template<class T> 41 ListNode<T>* myList<T>:: Last() 42 { 43 ListNode<T>* cur = head; 44 while(cur->next != NULL) 45 { 46 cur = cur->next; 47 } 48 return cur; 49 } 50 51 template<class T> 52 void myList<T>::Insert(ListNode<T>* x, T data) 53 { 54 if(x != NULL) 55 { 56 ListNode<T>* node = new ListNode<T>(data); 57 node->next = x->next; 58 x->next = node; 59 } 60 } 61 62 template<class T> 63 void myList<T>::Attach(T data) 64 { 65 ListNode<T>* last = Last(); 66 Insert(last, data); 67 } 68 69 template<class T> 70 void myList<T>::Delete(T data) 71 { 72 ListNode<T>* node = Find(data); 73 ListNode<T>* prev = FindPrev(data); 74 if(node != NULL && prev != NULL) 75 { 76 prev->next = node->next; 77 delete node; 78 } 79 } 80 81 template<class T> 82 void myList<T>::Print() 83 { 84 ListNode<T>* cur = head->next; 85 while(cur != NULL) 86 { 87 cout<<cur->data<<endl; 88 cur = cur->next; 89 } 90 cout<<endl; 91 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /*主程序*/ 2 #include "stdafx.h" 3 #include "listNode.h" 4 #include "myList.h" 5 #include "myList.cpp" 6 #include <iostream> 7 8 int _tmain(int argc, _TCHAR* argv[]) 9 { 10 myList<int> l; 11 l.Attach(1); 12 l.Attach(2); 13 l.Attach(3); 14 l.Attach(4); 15 l.Attach(5); 16 l.Print(); 17 18 ListNode<int>* node = l.Find(2); 19 l.Insert(node, 10); 20 l.Print(); 21 22 l.Delete(10); 23 l.Print(); 24 25 system("pause"); 26 return 0; 27 }
当然myList类中对链表的操作可以包含很多,通常为了使类保持紧凑性,同时为了适应不同的需求和方便用户添加新的成员函数,可以将对链表的遍历相关的操作另外封装为一个类,这里就不再细讲了,如果有兴趣可以参见东南大学金远平教授编著的《数据结构(C++描述)》第三章(P71)。
在随后的链表章节中,为了使算法的表述更加直观,就将ListNode和myList类中的成员变量设为public权限。