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


    单链表,可能有环,也可能无环。给定两个单链表的头节点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;
        }
        
        
        
        
        
    }
  • 相关阅读:
    二十一、正则表达式
    二十、冒泡算法,递归,装饰器
    十九、python内置函数汇总
    Jenkins-[--4--]-浏览器不能打开jenkins报告,报错Opening Robot Framework report failed
    Jenkins-[--3--]-robotframework脚本,配置自动发送邮件
    Jenkins-[--2--]-执行本地的robotframework项目
    Jenkins-[--1--]-环境配置
    Redis常用数据类型介绍、使用场景及其操作命令
    angular过滤器
    jscode属性排序
  • 原文地址:https://www.cnblogs.com/toov5/p/7502287.html
Copyright © 2020-2023  润新知