【链表篇】链表面试题集

来源:互联网 发布:追回前男友的套路知乎 编辑:程序博客网 时间:2024/05/20 18:01

原文:http://blog.csdn.net/dengsi23/article/details/7984291

1. 给定单链表,检测是否有环。如果有环,则求出进入环的第一个节点。

         判断单向链表是否有环,可以采用快指针与慢指针的方式来解决。即定义一个快指针fast和一个慢指针slow,使得fast每次跳跃两个节点,slow每次跳跃一个节点。如果链表没有环的话,则slow与fast永远不会相遇(这里链表至少有两个节点);如果有环,则fast与slow将会在环中相遇。判断出链表有环以后,则需要算出进入环的第一个节点。在fast与slow第一次相遇后,设置一个节点pNode从链表的头部开始遍历,每次只遍历一个节点。这样,当fast与slow再次相遇时,pNode所处的位置便是环的首部。以下该问题的实现源码(C语言描述):

[cpp] view plaincopy
  1. LNode* GetLoopNode(LNode* head)  
  2. {  
  3.     //前置条件的判断  
  4.     if (!head)  
  5.     {  
  6.         return NULL;  
  7.     }  
  8.   
  9.     //定义一个快指针和一个慢指针  
  10.     LNode* fast = head;  
  11.     LNode* slow = head;  
  12.     while (fast && (fast->next))  
  13.     {  
  14.         fast = fast->next->next;  
  15.         slow = slow->next;  
  16.   
  17.         if (fast == slow)  
  18.         {  
  19.             //如果有环,则返回环的第一个节点  
  20.             slow = head;  
  21.             while (1)  
  22.             {  
  23.                 fast = fast->next;  
  24.                 slow = slow->next;  
  25.   
  26.                 if (fast == slow)  
  27.                 {  
  28.                     break;  
  29.                 }  
  30.             }  
  31.   
  32.             return slow;  
  33.         }  
  34.     }  
  35.   
  36.     return NULL;  
  37. }  

 注:原文并未解释在找到环后,为啥通过齐步走,能找到环的首部。个人觉得,此处可以这样简单理解,把链表表头代表的链缠绕到环上。

2. 给定两个单链表,检测两个链表是否有交点,如果有返回第一个交点。

        检测两个单链表是否有交点是很容易的,因为如果两个链表有交点,那么这两个链表的最后一个节点必须相同,所以只需比较最后一个节点即可。但是这种方案是无法求出第一个交点的,所以需要另辟蹊径。另外,判断是否有交点可以转换成链表是否有环的问题。让一个链表的最后一个节点指向第二个链表的头结点,这样的话,如果相交,则新的链表是存在环的,并且交点便是环的入口点。

3. 只给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点。

        由于没有给定链表的头结点,所以无法获取需要删除节点的前驱结点,这样就无法使用常规的方法去解决。在《编程之美》这本书里面给出一个非常精妙的解法,也就是采用了狸猫换太子的思想。以下是实现源码:

[cpp] view plaincopy
  1. void DeleteNode(LNode* pNode)  
  2. {  
  3.     //如果pNode是最后一个节点,则该问题误解  
  4.     if (!pNode || !(pNode->next))  
  5.     {  
  6.         return;  
  7.     }  
  8.   
  9.     //获取pNode的后驱节点  
  10.     LNode* qNode = pNode->next;  
  11.   
  12.     //将qNode的值赋给pNode,并删除qNode  
  13.     pNode->data = qNode->data;  
  14.     pNode->next = qNode->next;  
  15.     free(qNode);  
  16. }  

 

4. 给定单链表头结点,删除链表中倒数第k个结点

        这个问题的关键是需要获取倒数第k个节点。如何获取呢?这里,需要两个指针p和q,p指向头结点,q指向距离头结点为k的节点。这样p和q每次遍历一个节点,当q遍历到末尾的时候,p指向的节点即为倒数第k个节点。然后删除即可。以下是实现源码(这里获取的是倒数第k+1个节点,因为需要删除的是倒数第k个节点):

[cpp] view plaincopy
  1. void DelLastKNode(unsigned int k, LNode* head)  
  2. {  
  3.     //前置条件判断  
  4.     if (!head || k <= 0)  
  5.     {  
  6.         return;  
  7.     }  
  8.   
  9.     LNode* pNode = head;  
  10.     LNode* qNode = head;  
  11.   
  12.     //将qNode遍历到正数第k + 1个节点  
  13.     unsigned int i;   
  14.     for (i = 0; i < k; i++)  
  15.     {  
  16.         qNode = qNode->next;  
  17.     }  
  18.   
  19.     //获取倒数第k + 1个节点  
  20.     while (qNode->next)  
  21.     {  
  22.         pNode = pNode->next;  
  23.         qNode = qNode->next;  
  24.     }  
  25.   
  26.     //删除倒数第k个节点  
  27.     LNode* temp = pNode->next;  
  28.     pNode->next = temp->next;  
  29.     free(temp);  
  30. }  

 注: 个人觉得原文此处不严谨。

  1.     unsigned int i;   
  2.     for (i = 0; i < k; i++)  
  3.     {  
  4.         qNode = qNode->next;  
  5.     }  
  6.   
可改为:

  1.     unsigned int i;   
  2.     for (i = 0; i < k; i++)  
  3.     {  
  4.         qNode = qNode->next;  
  5.         if (!qNode)
  6.             return; // 不超过k+1个结点时,直接退出。
  7.     }  
  8.   

另外,个人觉得此函数在处理刚好只有k个结点时,会需到问题。因为它需要获取的是倒数k+1个结点。

相比之下,另一种方法,获取倒数第k个结点,然后用狸猫换太子之法删掉则能处理掉此种情况。

0 0
原创粉丝点击