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 }