算法总结之 两个单链表相交的一些列问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法总结之 两个单链表相交的一些列问题相关的知识,希望对你有一定的参考价值。

单链表,可能有环,也可能无环。给定两个单链表的头节点head1 和 head2 这两个链表可能交也可能不交。实现函数,如果相交,请返回相交的第一个节点,不交返回null

 

这道题目需要分析的情况非常

 

  本题拆分长三个子问题,每一个子问题都可以作为一道独立的算法题。

问题一、  如何判断有环   有则返回第一个进入环的节点 没有返回null

问题二、 如何判断两个无环链表是否相交。相交返回第一个相交节,不相交则返回null

问题三、 如何判断两个有环链表是否相交。相交返回第一个相交节,不相交则返回null

 

注意! 如果一个链表有环,另一个链表无环,他们是不可能相交的。

 

如果一个链表没有环,遍历一遍可以遇到链表的终点。有环,那么。。。。永远在环里。。。。 如何找到第一个入环节点

    思想!!!!!  设置一个慢指针 slow 和一个快指针 fast   开始都是head位置,然后slow 走一步 fast走两步 遍历

       如果无环,fast一定先遇到终点,一旦fast到达终点,说明链表是无环的。

      如果有环,fast和slow一定会在环中的某个位置相遇,当fast和slow相遇时候,fast指针重新回到head位置,slow不动。fast从每次移动两步,改为一步,slow依然一步。继续遍历

      fast和slow一定会再次相遇。并且在第一个入环的节点处相遇。

 

瞅瞅 getLoopNode方法

package TT;

import TT.Test84.Node;

public class Test99 {
  
    public Node getLoopNode(Node head){
        if(head==null || head.next==null || head.next.next==null){
            return null;
        }
        Node n1 = head.next;
        Node n2 = head.next.next;
        while(n1!=n2){
            if(n2.next == null || n2.next.next==null){
                return null;
            }
            n2 = n2.next;
            n1 = n1.next;
        }
        
        n2 = head;
        while(n1 != n2){
            n1=n1.next;
            n2=n2.next;
        }
        return n1;
    }
    
    
    
}

如果解决了问题一,则知道了两个链表有环和无环的情况。

    一个有环 一个无环  那么这两个链表是无论如何也不可能相交的 

    能相交的情况分两种: 一种是 都无环  一种是都有环

问题二   判断两个无环链表相交  交则返回第一个相交节点 不则返回null

 

  如果两个无环链表相交 那么从相交节点开始一直到两个链表终止的这一段 是连个链表共享的

  解决: 

   1 链表从1从头节点开始,走到最后一个节点(不是结束) 统计链表1长度len1 同时记录链表1的最后一个节点end1

   2  链表从2从头节点开始,走到最后一个节点(不是结束) 统计链表2长度len2 同时记录链表1的最后一个节点end2

   3 如果 end1 != end2 说明两个链表不交 返回null  如果end1=end2 说明相交

  4 如果链表1比较长,链表1就先走len1-len2步。如果链表2比较长,链表1就先走len2-len1步。  然后两个链表一起走,第一次走到一起的那个就是所求

实现代码:

  

package TT;


public class Test {
    
public class Node{
        
        public int value;
        public Node next;
        
        public Node(int data){
            this.value=data;
        }
        
        
    }
    
  public Node noLoop(Node head1, Node head2){
      if(head1==null || head2 ==null){
          return null;
      }
      Node cur1 = head1;
      Node cur2 = head2;
      int n =0;
      
      while(cur1.next != null){
          n++;
          cur1 = cur1.next;
          
      }
      while(cur2.next != null){
          n--;
          cur2=cur2.next;
      }
      
      if(cur1 != cur2){
          return null;
      }
      cur1 = n>0 ? head1 : head2;
      cur2 = cur1 == head1 ? head2 : head1;
      
      n=Math.abs(n);
      while(n!=0){
          n--;
          cur1 = cur1.next;
      }
      
      while(cur1 != cur2){
          cur1 = cur1.next;
          cur2 = cur2.next;
      }
      return cur1;
  }
  
  
  
}

问题三,如何判断两个有环链表是否相交,交则返回第一个相交节点,不则返回null

 考虑问题三时候,已经得到了两个链表各自的第一个入环节点

    拓扑结构的判断:

       技术分享

让链表1从loop1出发,因为loop1之后的所有节点都在环上,所以将来一定能回到loop1 如果回到loop1之前没有遇到loop2 不想交(左图)

如果遇到了loop2 说明相交(右图)  此时返回loop1 跟 loop2 都可以

代码:

package TT;

public class Test100 {

public class Node{
        
        public int value;
        public Node next;
        
        public Node(int data){
            this.value=data;
        }
    
    }
    

public Node bothLoop(Node head1 ,Node loop1, Node head2, Node loop2){
    
        Node cur1 = null;
        Node cur2 = null;
        if(loop1 == loop2){
            cur1 = head1;
            cur2 = head2;
            int n=0;
            while(cur1 != loop1){
                n++;
                cur1 = cur1.next;
            }
            while(cur2 !=loop2){
                n--;
                cur2=cur2.next;
            }
            cur1 = n>0 ? head1 : head2;
            cur2 = cur1==head1? head2 : head1;
            while(n!=0){
                n--;
                cur1=cur1.next;
            }
            while(cur1 != cur2){
                cur1 =cur1.next;
                cur2 =cur2.next;
            }
            return cur1;
        }else{
            cur1 = loop1.next;   //进环测试
           while(cur1 != loop1){
               if(cur1==loop2){
                   return loop1;
               }
               cur1=cur1.next;
           }
            return null;
        }
    
}


    
    
}

全部代码:

package TT;

public class Test101 {

    public class Node{
        public int value;
        public Node nextNode;
        
        public Node(int data){
            this.value=data;
        }
    }
    
    public Node getIntersectNode(Node head1, Node head2){
        
        if(head1 == null || head2==null){
            return null;
        }
        
        Node loop1= getLoopNode(head1);  //获得环形节点
        Node loop2 =getLoopNode(head2);
        if(loop1==null && loop2==null){
            return noLoop(head1, head2);
        }
        if(loop1 !=null && loop2 != null){
            return bothLoop(head1, loop1, head1 loop2);
        }
        return null;
    }
    
    
    
    
    
}

 

以上是关于算法总结之 两个单链表相交的一些列问题的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法面试之链表问题集锦(下)

算法面试

算法总结之 两个单链表生成相加的链表

算法总结之 合并两个有序的单链表

数据结构与算法面试题80道

判断两个单链表是否相交及相交的第一个节点