1 // 链表专题.cpp : 定义控制台应用程序的入口点。
2 //
3
4 /*************************************************
5 设计者:cslave
6 版本说明:本代码免费用于商用,拷贝,转移,使用,但是
7 有本代码导致的问题,本人概不负责。
8 设计时间:2012.6.25
9 分发原则:遵守GNU规范。
10 本专题主要针对链表的相关面试题进行展开,中间涉及一些
11 经典的链表面试题,有相关代码设计,使用,学习,分发本
12 代码,请注明本博客的地址
13 http://www.cnblogs.com/cslave/
14
15 该篇博文源于看何海涛老师的文章有感,去年何老师在CSDN举行
16 编程比赛送书活动,本人有幸得到一本,感恩何老师。
17 且代码中部分函数接口采用何老师的设计。
18 专题中存在很多不足,希望大家能够帮助改进,谢谢。
19 ****************************************************/
20
21
22 #include "stdafx.h"
23 #include "List.h"
24 #include <stdlib.h>
25 #include <iostream>
26 using namespace std;
27
28
29 ListNode* CreateListNode(int Value) //链表节点创建
30 {
31 ListNode* pNode= new ListNode();
32 pNode->Data=Value;
33 pNode->pNextNode=NULL;
34 return pNode;
35 }
36 /***************************************************
37
38
39 下面这个函数接口能够非常方便的创建链表,尤其是带环链表
40 。一个函数将两个独立的节点联系起来,使用灵活方便。
41 其中的好处自己去体会吧。
42
43 ****************************************************/
44 void ConnectListNodes(ListNode* pCurrent,ListNode* pNext)
45 {
46 if(pCurrent==NULL)
47 {
48 throw new exception("Failed to Connect Nodes!");
49 }
50 pCurrent->pNextNode=pNext;
51 }
52
53 /******************************************************
54
55 打印节点函数
56
57 ******************************************************/
58
59 void PrintNode(ListNode* pNode)
60 {
61 if(pNode==NULL)
62 {
63 cout<<"The Node Is Empty!"<<endl;
64 }
65 else
66 cout<<"The Value Of The Node Is:"<<pNode->Data<<endl;
67 }
68
69 /******************************************************
70
71 打印链表函数
72
73 ******************************************************/
74
75 void PrintList(ListNode* pHead)
76 {
77 cout<<"List Print Begin!:"<<endl;
78 if(!pHead)
79 return;
80 ListNode* pTemp=pHead;
81 while(pTemp)
82 {
83 cout<<pTemp->Data<<"--->";
84 pTemp=pTemp->pNextNode;
85 }
86 cout<<"List Print End!:"<<endl;
87 }
88
89 /******************************************************
90
91 为链表添加尾节点函数
92
93 ******************************************************/
94
95 void AddTail(ListNode** pHead,int Value)
96 {
97 ListNode *pNode=new ListNode();
98 pNode->Data=Value;
99 pNode->pNextNode=NULL;
100
101 if(*pHead==NULL) //无节点
102 *pHead=pNode;
103 else
104 {
105 ListNode* pTemp=*pHead;
106 while(pTemp->pNextNode!=NULL)
107 {
108 pTemp=pTemp->pNextNode;
109 }
110 pTemp->pNextNode=pNode;
111 }
112 }
113
114 /******************************************************
115
116 移除节点函数,移除定点值节点。复杂度O(n)
117
118 ******************************************************/
119
120 void RemoveNode(ListNode** pHead,int Value)
121 {
122 if(pHead==NULL||*pHead==NULL)
123 return;
124 ListNode* pToBeDelete=NULL;
125 if((*pHead)->Data==Value)
126 {
127 pToBeDelete=*pHead;
128 *pHead=(*pHead)->pNextNode;
129 }
130 else
131 {
132 ListNode *pNode=*pHead;
133 while(pNode->pNextNode!=NULL&&pNode->pNextNode->Data!=Value)
134 {
135 pNode=pNode->pNextNode;
136 }
137 if(pNode->pNextNode!=NULL&&pNode->pNextNode->Data==Value)
138 {
139 pToBeDelete=pNode->pNextNode;
140 pNode->pNextNode=pNode->pNextNode->pNextNode;
141 }
142 }
143 if(pToBeDelete!=NULL)
144 {
145 delete pToBeDelete;
146 pToBeDelete=NULL;
147 }
148 }
149
150
151 /******************************************************
152
153 移除节点函数,移除定点值节点。复杂度O(1)
154 这个函数的思想是,将删除节点的下一个节点的值赋给自己,这样
155 狸猫换太子,将自己成为合法的节点,这样只需删除下一个节点。
156 还有一个问题:如果删除的结点位于链表的尾部,没有下一个结点,
157 怎么办?我们需要遍历得到删除结点的前序结点。这个时候时间复杂度是O(n)。
158
159 假设链表总共有n个结点,
160 我们的算法在n-1总情况下时间复杂度是O(1),只有当给定的结点处于链表
161 末尾的时候,时间复杂度为O(n)。那么平均时间复杂度[(n-1)*O(1)+O(n)]/n,
162 仍然为O(1)。
163 ******************************************************/
164
165 void DeleteNode(ListNode** pHead,ListNode* pToBeDelete) //删除链表定节点 O(1)算法
166 {
167 if(!pHead||!pToBeDelete)
168 return;
169 if(pToBeDelete->pNextNode!=NULL)
170 {
171 ListNode *pNext=pToBeDelete->pNextNode;
172 pToBeDelete->Data=pNext->Data;
173 pToBeDelete->pNextNode=pNext->pNextNode;
174 delete pNext;
175 pNext=NULL;
176 }
177 else if(*pHead==pToBeDelete)
178 {
179 delete pToBeDelete;
180 pToBeDelete=NULL;
181 *pHead=NULL;
182 }
183 else
184 {
185 ListNode *pNode=*pHead;
186 while(pNode->pNextNode!=pToBeDelete)
187 {
188 pNode=pNode->pNextNode;
189 }
190 pNode->pNextNode=NULL;
191 delete pToBeDelete;
192 pToBeDelete=NULL;
193 }
194 }
195
196 /******************************************************
197
198 查找链表倒数第K个节点,仅需遍历一遍链表。
199 遍历时维持两个指针,第一个指针从链表的头指针开始遍历,
200 在第k-1步之前,第二个指针保持不动;在第k-1步开始,
201 第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在k-1,
202 当第一个(走在前面的)指针到达链表的尾结点时,
203 第二个指针(走在后面的)指针正好是倒数第k个结点。
204
205 ******************************************************/
206
207 ListNode* FindKthToTail(ListNode* pHead,unsigned int k)//倒数第k个节点
208 {
209 if(!pHead||k==0)
210 return NULL;
211 ListNode *pFront=pHead;
212 ListNode *pBack=pHead;
213 for(unsigned int i=0;i< k-1;i++)
214 {
215 if( pFront->pNextNode!=NULL)
216 pFront=pFront->pNextNode;
217 else
218 {
219 return NULL;
220 }
221 }
222 while(pFront->pNextNode!=NULL)
223 {
224 pFront=pFront->pNextNode;
225 pBack=pBack->pNextNode;
226 }
227 return pBack;
228 }
229
230
231 /******************************************************
232
233 求链表中间点函数,也很简单,设置两个指针,一个走两步,另外
234 一个走一步,这样第二个到链表尾时,第一个刚好一半,即为链表
235 中间点。
236
237 ******************************************************/
238
239 //求链表的中间节点
240
241 ListNode* FindMiddleList(ListNode *pHead)
242 {
243 if(pHead==NULL)
244 return NULL;
245 if(!(pHead->pNextNode))
246 return pHead;
247 ListNode *pFront=pHead;
248 ListNode *pBack=pHead;
249 unsigned int i=1;
250 while(pFront->pNextNode)
251 {
252 pFront=pFront->pNextNode;
253 if(!(i&1))
254 pBack=pBack->pNextNode;
255 i++;
256 }
257 return pBack;
258 }
259
260 /******************************************************
261
262 判断链表是否有环函数,想法是设置两个指针,其中一个指针走两步,
263 另外一个指针走一步,这样这样如果有环,他们一定会相遇,如果没有环
264 则两步指针会在O(n)量级完成判断。有环也在同样量级,但是小于n,
265 比如环很大的时候。
266
267 ******************************************************/
268
269 //求链表是否有环
270 bool JudgeListRing(ListNode* pHead)
271 {
272 if(!pHead||pHead->pNextNode==NULL) //单节点可能是环
273 return false;
274 if(pHead->pNextNode==pHead) //单节点环
275 return true;
276 ListNode* pFront=pHead;
277 ListNode* pBack=pHead;
278 while(pFront->pNextNode->pNextNode!=NULL)
279 {
280 pBack=pBack->pNextNode;
281 pFront=pFront->pNextNode->pNextNode;
282 if(pFront==pBack)
283 return true;
284 if(pFront->pNextNode==NULL)
285 break;
286 }
287 return false;
288 }
289
290
291 /******************************************************
292
293 求环的起始点位置函数,这个我先来推导一个关系式。
294 设两个指针,同样一个一步,一个二步,设一步指针和二步指针相遇时
295 一步指针走了s 则二步指针走了2s ,他们相遇时必然在环里相遇,因为
296 环外无法相遇,设环长为r 则相遇时,二步指针比一步指针多走了nr,
297 n为整数且大于等于1.
298 2s=s+nr;
299 再设环起点到出发点为a,相遇时一步指针在环内走了x,链表总长度为L
300 则有:
301 s=a+x
302 a=s-x=nr-x=(n-1)r+r-x=(n-1)r+L-a-x
303 好了 我得到这个式子:
304 a=(n-1)r+L-a-x
305 (L – a – x)为相遇点到环入口点的距离,由此可知,
306 从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,
307 于是我们从链表头、与相遇点分别设一个指针,每次各走一步,
308 两个指针必定相遇,且相遇第一点为环入口点。
309
310 ******************************************************/
311
312 //求链表环起点位置
313
314 ListNode* CalculateListRingPos(ListNode* pHead)
315 {
316 if(!JudgeListRing(pHead))
317 return NULL;
318 if(pHead->pNextNode==pHead||pHead->pNextNode->pNextNode==pHead)
319 return pHead;
320 ListNode *pFront=pHead;
321 ListNode *pBack =pHead;
322 ListNode *pComp =pHead;
323 while(pFront->pNextNode->pNextNode!=NULL)
324 {
325 pBack=pBack->pNextNode;
326 pFront=pFront->pNextNode->pNextNode;
327 if(pFront==pBack)
328 break;
329 }
330 if(pFront==pBack)
331 {
332 while(pFront!=pComp)
333 {
334 pFront=pFront->pNextNode;
335 pComp=pComp->pNextNode;
336 }
337 return pFront;
338 }
339 return NULL;
340 }
341
342 //链表反转
343 ListNode* ReverseList(ListNode* pHead)
344 {
345 ListNode* pReverseNode=NULL;
346 ListNode* pNode=pHead;
347 ListNode* pPrev=NULL;
348 while(pNode!=NULL)
349 {
350 ListNode* pNext=pNode->pNextNode;
351 if(pNext==NULL)
352 pReverseNode=pNode;
353 pNode->pNextNode=pPrev;
354 pPrev=pNode;
355 pNode=pNext;
356 }
357 return pReverseNode;
358 }
359
360 /******************************************************
361 判断两个链表相交的函数,两个链表相交分为4种情况
362 1 两个链表都没有环 只需要检查尾节点是否相等
363 2,3 其中一个链表有环,另外一个没有环,这种不可能相交
364 4 两个链表都有环,这里,两个链表相交地点必然是环入口点
365 或者入口点之前,想想为什么?
366 若链B和链A都有环,且链B在链A环内相交,则链A入口点到相交点的
367 元素不在环B上,那么链B不存在该环。
368 所以仅需判断两个链表环入口点是否相等,即可判断是否相交。
369
370 ******************************************************/
371
372 //判断两个链表是否相交
373
374 bool JudgeListCross(ListNode *pHead1,ListNode *pHead2)
375 {
376 if(!pHead1||!pHead2) return false;
377 bool judge1=JudgeListRing(pHead1);
378 bool judge2=JudgeListRing(pHead2);
379 int Result=judge1*2+judge2;
380 ListNode *pNode1=pHead1;
381 ListNode *pNode2=pHead2;
382 ListNode *pRing1=NULL;
383 ListNode *pRing2=NULL;
384 ListNode *pRing=NULL;
385 switch(Result)
386 {
387 case 0:
388 while(!pNode1->pNextNode)
389 pNode1=pNode1->pNextNode;
390 while(!pNode2->pNextNode)
391 pNode2=pNode2->pNextNode;
392 if(pNode1==pNode2)
393 return true;
394 else
395 return false;
396 break;
397 case 1:
398 return false;
399 break;
400 case 2:
401 return false;
402 break;
403 case 3:
404 pRing1=CalculateListRingPos(pHead1);
405 pRing2=CalculateListRingPos(pHead2);
406 pRing=pRing1;
407 while(pRing1->pNextNode!=pRing)
408 {
409 if(pRing1==pRing2)
410 return true;
411 else
412 pRing1=pRing1->pNextNode;
413 }
414 return false;
415 break;
416 default:
417 break;
418 }
419 return false;
420 }
421
422 /******************************************************
423
424 带环链表销毁函数,要注意是带环的,所以写的较复杂些。
425
426 ******************************************************/
427
428 void DestroyList(ListNode *pHead)
429 {
430 ListNode *pNode=pHead;
431 ListNode *pTemp=NULL;
432 int i=0;
433 if(JudgeListRing(pHead))
434 pTemp=CalculateListRingPos(pHead);
435 while(pNode!=NULL)
436 {
437 pHead=pHead->pNextNode;
438 if(pNode!=pTemp)
439 delete pNode;
440 else
441 {
442 if(!i)
443 {
444 delete pNode;
445 i++;
446 }
447 else
448 return;
449
450 }
451 pNode=pHead;
452 }
453 }
454
455
456 void Test1()
457 {
458 printf("=====Test1 测试链表的相关的操作=====\n");
459 ListNode* pNode1 = CreateListNode(1);
460 ListNode* pNode2 = CreateListNode(2);
461 ListNode* pNode3 = CreateListNode(3);
462 ListNode* pNode4 = CreateListNode(4);
463 ListNode* pNode5 = CreateListNode(5);
464
465 ConnectListNodes(pNode1, pNode2);
466 ConnectListNodes(pNode2, pNode3);
467 ConnectListNodes(pNode3, pNode4);
468 ConnectListNodes(pNode4, pNode5);
469 AddTail(&pNode1,6);
470 RemoveNode(&pNode1,5);
471 DeleteNode(&pNode1,pNode4);
472 printf("expected result: 3.\n");
473 ListNode* pNode = FindKthToTail(pNode1, 2);
474 PrintNode(pNode);
475 AddTail(&pNode1,7);
476 printf("expected result: 3.\n");
477 ListNode* Node=FindMiddleList(pNode1);
478 PrintNode(Node);
479 printf("链表反转后的结构为!\n");
480 Node=ReverseList(pNode1);
481 PrintList(Node);
482
483 DestroyList(pNode1);
484 }
485
486
487 void Test2()
488 {
489 printf("=====Test2 测试链表的是否存在环及位置=====\n");
490 ListNode* pNode1 = CreateListNode(1);
491 ListNode* pNode2 = CreateListNode(2);
492 ListNode* pNode3 = CreateListNode(3);
493 ListNode* pNode4 = CreateListNode(4);
494 ListNode* pNode5 = CreateListNode(5);
495
496 ConnectListNodes(pNode1, pNode2);
497 ConnectListNodes(pNode2, pNode3);
498 ConnectListNodes(pNode3, pNode4);
499 ConnectListNodes(pNode4, pNode5);
500 ConnectListNodes(pNode5, pNode3);
501 bool Exist= JudgeListRing(pNode1);
502 ListNode * Node=NULL;
503 if(Exist)
504 {
505 printf("链表存在环!入口点为:\n");
506 printf("期望的入口点为3:\n");
507 Node=CalculateListRingPos(pNode1);
508 PrintNode(Node);
509 }
510 else
511 printf("链表不存在环!\n");
512
513 DestroyList(pNode1);
514
515 }
516
517 void Test3()
518 {
519 printf("=====Test3 测试两个链表是否相交=====\n");
520 ListNode* pNode1 = CreateListNode(1);
521 ListNode* pNode2 = CreateListNode(2);
522 ListNode* pNode3 = CreateListNode(3);
523 ListNode* pNode4 = CreateListNode(4);
524 ListNode* pNode5 = CreateListNode(5);
525 ListNode* pNode6 = CreateListNode(6);
526 ListNode* pNode7 = CreateListNode(7);
527 ListNode* pNode8 = CreateListNode(8);
528 ListNode* pNode9 = CreateListNode(9);
529
530 ConnectListNodes(pNode1, pNode2);
531 ConnectListNodes(pNode2, pNode3);
532 ConnectListNodes(pNode3, pNode4);
533 ConnectListNodes(pNode4, pNode5);
534 ConnectListNodes(pNode5, pNode3);
535 ConnectListNodes(pNode6, pNode7);
536 ConnectListNodes(pNode7, pNode8);
537 ConnectListNodes(pNode8, pNode9);
538 ConnectListNodes(pNode9, pNode3);
539 bool Exist=JudgeListCross(pNode1,pNode6);
540 if(Exist)
541 {
542 printf("两个链表相交:\n");
543 }
544 else
545 printf("两个链表不相交!\n");
546 }
547
548 int _tmain(int argc, _TCHAR* argv[])
549 {
550 Test1();
551 Test2();
552 Test3();
553 return 0;
554 }