基本数据结构实现–单链表【含测试代码】

基本数据结构实现–单链表

链表是线性表的另一种实现方式。与顺序表不同,链表中逻辑上相邻的数据元素在物理上未必相邻,而是通过一个指针指明下一个元素的

物理地址。单链表中节点类型的描述如下:

 

1 struct LNode {
2     ElemType data;  //节点数据域
3     LNode* next;    //指向下一个节点的指针
4 };
5 typedef LNode* LinkList;

单链表的优点:与顺序表相比,单链表的优点在于插入和删除操作:虽然单链表的插入和删除操作与顺序表一样时间复杂度都是O(n),但是对于单链表来说,

插入和删除时的基本操作主要是“比较”(对于按位插入删除,比较是否到了第i位;对于按值操作,比较值是否相等),而顺序表插入和删除时

的基本操作主要是“移动”。从硬件的角度讲,做一次比较运算的时间是远小于赋值运算的。因此,链表的插入和删除操作要高效得多。

 

单链表的缺点:由于单链表中逻辑上相邻的元素在物理上未必相邻,故不能根据第一个元素的地址立即计算出第i个元素的地址。于是单链表不支持随机访问。

(所谓随机访问是指:根据起始地址加上元素序号就可以立即找到任意一个元素。)要在单链表中访问第i个元素,必须一个一个访问链表中每一个元素直到

第i个。

 

以下代码实现一个带头节点的单链表,并尝试实现一种重要操作:将链表反序

  1 /* 带头节点的单链表
  2 
  3      实现操作:
  4      *1 判空
  5      *2 求表长
  6      *3 按位序查找
  7      *4 按值查找
  8      *5 前插操作
  9      *6 后插操作
 10      *7 按位插入
 11      *8 删除指定节点
 12      *9 按位删除
 13      *10 单链表的建立-头插和尾插
 14                                 */
 15 #include <iostream>
 16 #include <cstdio>
 17 using namespace std;
 18 
 19 typedef int ElemType;
 20 typedef struct LNode {
 21     ElemType data;  //节点数据域
 22     LNode* next;    //指向下一个节点的指针
 23 } * LinkList;
 24 
 25 bool Empty(LinkList& L)
 26 {
 27     if( L->next == NULL )
 28         return true;
 29     return false;
 30 }
 31 
 32 //求表的长度
 33 int Length(LinkList L)
 34 {
 35     int len = 0;
 36     LNode* p = L;
 37     //始终让p指向第j个节点,当p没有下一个节点时,j就是表的长度
 38     while( p->next != NULL ) {
 39         len++;
 40         p = p->next;
 41     }
 42     return len;
 43 }
 44 
 45 //按位序查找,返回指向第i个节点的指针
 46 //查找失败时返回的是NULL
 47 LNode* GetElem(LinkList L,int i)
 48 {
 49     if( i < 0 )
 50         return NULL;
 51     int j = 0;
 52     LNode* p = L;
 53     while( j < i && p != NULL ) {
 54         j++;
 55         p = p->next;
 56     }
 57     return p;   //i=0时返回的是头节点,i值大于表长是返回的是空指针
 58 }
 59 
 60 //按值查找,找到数据域为e的节点,返回其指针
 61 LNode* LocateElem(LinkList L,ElemType e)
 62 {
 63     LNode* p = L->next; //注意不是LNode* p = L :因为指向头节点,数据域是未知的
 64     while( p != NULL && p->data != e ) {
 65         p = p->next;
 66     }
 67     return p;   //若e不存在,返回空指针
 68 }
 69 
 70 //前插操作:在p节点之前插入元素e
 71 bool InsertPriorNode(LNode* p,ElemType e)
 72 {
 73     if( p == NULL )
 74         return false;
 75 
 76     //偷天换日
 77     LNode* s = new LNode;
 78     s->next = p->next;
 79     p->next = s;
 80     s->data = p->data;
 81     p->data = e;
 82     return true;
 83 }
 84 
 85 //后插操作:在p节点之后插入元素e
 86 bool InsertNextNode(LNode* p,ElemType e)
 87 {
 88     if( p == NULL )
 89         return false;
 90     LNode* s = new LNode;
 91     s->data = e;
 92     s->next = p->next;
 93     p->next = s;
 94     return true;
 95 }
 96 
 97 //按位插入:在单链表L的第i个位置插入元素e
 98 bool ListInsert(LinkList& L,int i,ElemType e)
 99 {
100     if( i < 1 )
101         return false;   //位序最小是1
102 
103     //先拿到指向第i-1个节点的指针,然后在他后面插入元素e
104     LNode* p = GetElem(L,i-1);
105     return InsertNextNode(p,e);
106 }
107 
108 //删除指定节点p
109 bool DeleteNode(LNode* p)
110 {
111     if( p == NULL )
112         return false;
113     //偷天换日
114     LNode* s = p->next;
115     //有bug:当p是最后一个节点时,无法删除(无法从p->next获取data)
116     //非要删除最后一个节点的话,可以传L头节点指针进来
117     p->data = s->data;
118     p->next = s->next;
119     delete s;
120 
121     return true;
122 }
123 
124 //按位删除:删除单链表L的位序为i的元素,并用e返回
125 bool ListDelete(LinkList& L, int i,int& e)
126 {
127     if( i < 1 )
128         return false;
129     LNode* p = GetElem(L,i-1);
130 
131     if( p == NULL ) //没有第i-1个节点,更没有第i个节点
132         return false;
133     if( p->next == NULL ) //找到了第i-1个节点,但是没有第i个节点
134         return false;
135     LNode* t = p->next;
136     e = t->data;
137     p->next = t->next;
138     delete t;
139     return true;
140 }
141 
142 //头插法建立单链表(假设所给数据从键盘输入)
143 LinkList List_HeadInsert(LinkList& L)
144 {
145     //假设元素为int型
146     int x;
147     L = new LNode;
148     L->next = NULL;
149     while( cin >> x ) {
150         InsertNextNode(L,x);
151     }
152     return L;
153 }
154 
155 //尾插法建立单链表(假设所给数据从键盘输入)
156 LinkList List_TailInsert(LinkList& L)
157 {
158     //假设元素为int型
159     int x;
160     L = new LNode;
161     L->next = NULL;
162     LNode* r = L;
163     while( cin >> x ) {
164         LNode* s = new LNode;
165         s->data = x;
166         r->next = s;
167         r = s;  //创建过程中不必将尾节点的next置空,因为后面还会有节点
168     }
169     r->next = NULL; //链表创建结束,最后一个节点的下一个节点置空
170     return L;
171 }
172 
173 //按顺序输出单链表中所有元素
174 void Print(LinkList L)
175 {
176     if( L == NULL )
177         return ;
178     LNode* iterater = L->next;
179     while( iterater != NULL ) {
180         cout << iterater->data << endl;
181         iterater = iterater->next;
182     }
183 }
184 
185 void test1()
186 {
187     //*测试使用a[i]=2*i的100个元素建立链表
188     int a[100];
189     for( int i=0; i<100; i++ ) {
190         a[i] = 2*i;
191     }
192     LinkList L = new LNode;
193     L->next = NULL;
194     //使用尾插法将以上数组插入链表L
195     LNode* r = L;
196     for( int i=0; i<100; i++ ) {
197 
198         LNode* s = new LNode;
199         s->data = a[i];
200         r->next = s;
201         r = s;
202     }
203     r->next = NULL;
204 
205     //*测试在值为20的元素后插入元素985211
206     LNode* t = LocateElem(L,20);    //按值查找
207     InsertNextNode(t,985211);
208 
209     //*测试删除位序为15的元素(值应该是26)
210     LNode* tt = GetElem(L,15);      //按位序查找
211     if( tt != NULL ) {
212         DeleteNode(tt);
213     }
214 
215     //*测试按位序删除位序为100的元素
216     int ttt = 0;
217     if( ListDelete(L,100,ttt) )
218         cout << "按位序删除成功,删除的元素是:" << ttt << endl;
219     Print(L);
220     cout << "-------------------------------" << endl;
221 
222 
223     //下面尝试将L反序 *****重要*****
224     //抓住链表中的第一个数据元素,一个一个往后进行头插,表头结点指针还是L
225     LNode* it = L->next;
226     L->next = NULL; //*****第一个数据已经抓住了;不加这句会导致表尾自己指向自己(而非指向NULL)
227 
228     //从链表的第一个节点开始,一个一个地在将表中所有节点重新插在表头
229     while( it != NULL ) {
230         LNode* nxt = it->next;
231         it->next = L->next;
232         L->next = it;   //头插法重构链表,即可将链表反序
233         it = nxt;
234     }
235     Print(L);
236 
237     //*测试查找不存在的元素返回空指针
238     if( LocateElem(L,999) == NULL ) {
239         cout << endl << "999不存在" << endl;
240     }
241 
242 }
243 
244 //测试头插法
245 void test2()
246 {
247     LinkList L;
248     List_HeadInsert(L);
249     Print(L);
250 }
251 
252 int main()
253 {
254 //    test1();
255 //    test2();
256     return 0;
257 }