海涛老师的面试题-作业-链表专题代码及讲解

View Code
1 ListNode 数据结构
2 
3 
4 struct ListNode
5 {
6     int Data;
7         ListNode* pNextNode;
8 };

 

View Code
  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 }

posted on 2012-06-28 18:32  北冥茶花开  阅读(195)  评论(0编辑  收藏  举报

导航