1. 程式人生 > >算法總結之 兩個單鏈表相交的一些列問題

算法總結之 兩個單鏈表相交的一些列問題

找到 開始 兩種 end 相交 說明 移動 一個 返回

單鏈表,可能有環,也可能無環。給定兩個單鏈表的頭節點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;
    }
    
    
    
    
    
}

算法總結之 兩個單鏈表相交的一些列問題