单链表是否相交,是否存在环

来源:互联网 发布:java实战案例 编辑:程序博客网 时间:2024/06/06 13:20
当看到判断两链表是否相交,判断链表是否存在环时,就感觉不知道从何下手,原因是不知道什么是链表相交,什么是链表存在环,所以当明白概念的时候,发现这两个问题并不难,而且,其实两个单链表是否相交是和链表中存在环是有关系的。

  一、判断链表是否存在环。

  一个链表存在环,指的是,某个节点的next指向的是链表中在他之前的节点,这样在链表尾部形成环。(这个概念很重要。)
  弄明白概念后,对于下面两个问题就比较好解决了。
问题1,如何判断链表中存在环?
问题2如果链表存在环,如何找到环的入口点?
  问题1解决办法,弄懂环的概念以后,就能轻易解决了。设置两个指针(slow,fast),初始值都只想头结点,然后slow每次前进一步,fast每次前进两步,如果链表存在环,则fast先进入环,slow后进入环,最后fast会赶上slow两个指针会相遇,如果不存在环,则fast到尾节点则为null了,这样就不会相遇了。具体代码如下:
[cpp] view plaincopyprint?
  1. bool IsExitsLoop(slist *head)
  2. slist *slow, *fast;
  3. slow = fas t= head;
  4. while(fast != null && fast->next != null){
  5. slow=slow->next;
  6. fast=fast->next->next;
  7. if(fast == slow){
  8. break;
  9. }
  10. }
  11. return (fast == null || fast->next == null) ?false : true;
bool IsExitsLoop(slist *head)  slist *slow, *fast;slow = fas t= head;while(fast != null && fast->next != null){    slow=slow->next;    fast=fast->next->next;    if(fast == slow){        break;    }} return (fast == null || fast->next == null) ? false : true;



  问题2解决办法,碰撞点p到连接点的距离=头指针到连接点的距离,则设两个指针分别从头指针开始走和从相遇点开始走,这样他们会在环入口点相遇。解释如下:
  当fast遇到slow时,slow还没走到尾节点,fast在环内走了n圈,设slow走了s个节点,则fast走了2s个节点,设环长为r则,fast走的距离等于slow走的距离加上在环内走的距离(nr),则有2s=s+nr,则s=nr。
  设链表长为L,头结点到环入口点的距离为a,环入口点到相遇点的距离为x,则有s=a+x, s=nr, a+x=nr, a+x=(n-1)r+r, r=L-a, a+x=(n-1)r+L-a, a=(n-1)r+L-a-x,a为起点到环入口点的距离,等于n-1个环的距离加上L-a-x为相遇点到换入口点的距离。于是设两个指针分别从头指针开始走和从相遇点开始走,这样他们会在环入口点相遇,则再次相遇的点为环的入口点。具体分析过程看后面的手绘图。具体代码如下:
[cpp] view plaincopyprint?
  1. slist* FindLoopPort(slist *head)
  2. {
  3. slist *slow = head, *fast = head;
  4. while ( fast && fast->next )
  5. {
  6. slow = slow->next;
  7. fast = fast->next->next;
  8. if ( slow == fast )break;
  9. }
  10. // 如果没有环则返回空
  11. if (fast == NULL || fast->next == NULL)
  12. return NULL;
  13. // 分别从头结点和相遇点开始走
  14. slow = head;
  15. // 两个节点一直走直到相遇
  16. while (slow != fast)
  17. {
  18. slow = slow->next;
  19. fast = fast->next;
  20. }
  21. // 返回相遇点
  22. return slow;
  23. }
slist* FindLoopPort(slist *head){    slist *slow = head, *fast = head;    while ( fast && fast->next )     {        slow = slow->next;        fast = fast->next->next;        if ( slow == fast ) break;    }    // 如果没有环则返回空    if (fast == NULL || fast->next == NULL)        return NULL;    // 分别从头结点和相遇点开始走    slow = head;    // 两个节点一直走直到相遇    while (slow != fast)    {         slow = slow->next;         fast = fast->next;    }    // 返回相遇点    return slow;}


  二、判断两个单链表是否相交。

  方法一,其实这个和链表中是否有环是有关联的。两个单链表相交是Y型相交,而不是X型相交。所以两个链表相交,把其中一个链表的头结点接在另一个链表的尾节点后面,如果两链表相交,则连接后形成一个环。否则不形成环。具体分析过程看后面的手绘图。所以判断方法和判断链表中是否存在环的方法一样,前面加一步把两个链表连接就行了。
  方法二,因为是Y型相交,则从如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,这样就可以采用以下办法。我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。
  接下来找出相交点。这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。具体代码如下:
[cpp] view plaincopyprint?
  1. Node *intersection(Node *head1, Node *head2)
  2. if(!head1 || !head2)
  3. return NULL;
  4. int len1 = 1, len2 = 1;
  5. Node *p = head1, *q=head2, *fast, *slow;
  6. bool result = false;
  7. //判断两个链表是否相交,同时记下各个链表的长度
  8. while(p->next){
  9. len1++;
  10. p=p->next;
  11. }
  12. while(q->next){
  13. len2++;
  14. q=q->next;
  15. }
  16. // 尾节点相同则为相交,否则不想交
  17. result=(p==q);
  18. if(!result){
  19. return NULL; // 不存在则返回NULL
  20. }else{
  21. // 链表长的先走lengthMax-lengthMin步
  22. int steps = abs(len1 – len2);
  23. if(len1 > len2){
  24. fast = head1;
  25. slow = head2;
  26. }else{
  27. fast = head2;
  28. slow = head1;
  29. }
  30. while(steps){
  31. fast = fast->next;
  32. steps –-;
  33. }
  34. // 之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。
  35. while(fast != slow){
  36. fast = fast->next;
  37. slow = slow->next;
  38. }
  39. return fast;
  40. }
Node *intersection(Node *head1, Node *head2)if(!head1 || !head2)   return NULL;int len1 = 1, len2 = 1;Node *p = head1, *q=head2, *fast, *slow;bool result = false;//判断两个链表是否相交,同时记下各个链表的长度while(p->next){    len1++;    p=p->next;}while(q->next){    len2++;     q=q->next;}// 尾节点相同则为相交,否则不想交result=(p==q);if(!result){    return NULL; // 不存在则返回NULL}else{    // 链表长的先走lengthMax-lengthMin步    int steps = abs(len1 – len2);    if(len1 > len2){        fast = head1;        slow = head2;    }else{        fast = head2;        slow = head1;    }    while(steps){        fast = fast->next;        steps –-;    }        // 之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。    while(fast != slow){        fast = fast->next;        slow = slow->next;    }    return fast;}


分析的图:
原创粉丝点击