剑指offer-15:单链表有趣问题-有环-环长度-环入口等(续)

来源:互联网 发布:word怎么合计数据 编辑:程序博客网 时间:2024/06/05 12:45

第15题要求找到倒数第k个结点。采用快慢出发不同的指针,卡出k-1的距离,当快指针到尾结点时,慢指针刚好在倒数第k个位置上。

还有一些相关题目比较有趣,类似的思路。

(1)求链表的中间结点

答:设置两个指针,移动快慢不同。快指针每次移动2步,慢指针每次移动1步。两者同时从头结点开始出发。当快指针到达尾结点时,走的路程是n,由于慢指针速度是其一半,则相同时间走的路程就是n/2,恰好位于链表的中间。

如果链表中的结点为奇数,则返回中间结点;如果链表中的结点为偶数,则返回中间两个结点的任意一个(前一个)。

// fast每次走2步,终点时:偶数时自身会为null,奇数时next会为nullwhile( fast != NULL && fast->next != NULL)  {        fast = fast->next->next;      slow = slow->next;  } 

(2)判断单向链表是否有环

答:若单链表有环,则可定义两个快慢指针。和操场跑圈类似,若有环,则快指针势必会追上慢指针,此时就是有环。若没有环,快指针会走到尾结点也不会和慢指针相遇,此时无环。

代码:

bool isLoop(pNode *pHead)  {      pNode *fast = pHead;      pNode *slow = pHead;      //如果无环,则fast先走到终点      //当链表长度为奇数时,fast->Next为空      //当链表长度为偶数时,fast为空      while( fast != NULL && fast->next != NULL)      {          fast = fast->next->next;          slow = slow->next;          // 若有环则会从此处跳出while          if(fast == slow)          {              break;          }      }      // 判断出while循环是相遇而跳,还是快指针到尾结点了退出?    if(fast == NULL || fast->next == NULL  )          return false;      else          return true;  }  

(3)若单链表有环,环的长度?

答:第一次相遇点开始,两指针同时跑,开始计数。到第二次相遇时,慢指针跑了一圈,快指针跑了2圈,停止计数。计数值即为一圈长度。

int loopLength(pNode *pHead)  {      // 判断是否有环    if(isLoop(*pHead) == false)          return 0;      pNode *fast = pHead;      pNode *slow = pHead;      int length = 0;      bool begin = false;      bool agian = false;      while( fast != NULL && fast->next != NULL)      {          fast = fast->next->next;          slow = slow->next;          //第2次相遇,停止计数,出循环          if(fast == slow && agian == true)              break;          //第一次相遇,开始计数          if(fast == slow && agian == false)          {                         begin = true;              agian = true;          }            //计数          if(begin == true)              ++length;              }     return length;  }  

(4)如何找到环的入口点?

答:由归纳推理可得,从第一次的相遇点处,到环入口的距离,等于头结点到环入口的距离。

这里写图片描述

假设头结点到入口处为L1,相遇点顺时针入口点为L3,入口点到相遇点为L2。则当两指针相遇时,慢指针走了(L1+L2),快指针速度是2倍,应该走的是2(L1+L2)。再根据图上关系,快指针走的还可以表示为(L1+L2+L3+L2),则

2(l1+l2)=l1+l2+l3+l2l1=l3

所以当第一次相遇时,从相遇点和头结点分别设置两个移动步长均为1的指针,当相遇时,相遇点即为入口点。

Node* findLoopEntrance(pNode *pHead)  {      pNode *fast = pHead;      pNode *slow = pHead;      while( fast != NULL && fast->next != NULL)      {          fast = fast->next->next;          slow = slow->next;          //如果有环,则fast会超过slow一圈          if(fast == slow)          {              break;          }      }      if(fast == NULL || fast->next == NULL)          return NULL;      slow = pHead;      while(slow != fast)      {          slow = slow->next;          fast = fast->next;      }      return slow;  }  

(5)如何判断两个链表(不带环)是否相交?

答:可将其中一个链表的尾指针指向头结点。从另一个链表查找是否有环。如果有环说明相交;没有环说明不相交。


参考资料

【1】http://blog.csdn.net/thefutureisour/article/details/8174313

阅读全文
0 0
原创粉丝点击