zoukankan      html  css  js  c++  java
  • 海涛老师的面试题作业链表专题代码及讲解

    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 }
  • 相关阅读:
    第一次团队作业——团队展示
    TAC队--团队选题报告
    TAC 坦克队
    Leetcode题库——16.最接近的三数之和
    Leetcode题库——11.盛最多水的容器
    Leetcode题库——23.合并k个排序链表
    Leetcode题库——4.寻找两个有序数组的中位数
    Leetcode题库——15.三数之和
    Leetcode题库——12.整数转罗马数字
    Leetcode题库——19.删除链表的倒数第 n 个节点【##】
  • 原文地址:https://www.cnblogs.com/cslave/p/2568589.html
Copyright © 2011-2022 走看看