1. 程式人生 > 實用技巧 >[程式設計題] nk 連結串列中的入環節點-快慢指標和雜湊方法

[程式設計題] nk 連結串列中的入環節點-快慢指標和雜湊方法

[程式設計題] nk:連結串列中的入環節點

題目描述

輸入輸出例子

思路

方法1、藉助雜湊表

思想:我們通過一個dummyNode不斷遍歷每一個節點,當我們每次遍歷到這個當前節點的時候就看他在不在雜湊表中,不在的話加入進去;在的時候就恰好這個節點就是入環節點。

時間複雜度:O(n)

Java程式碼

import java.util.*;
/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    //方法1:藉助雜湊表;
    //思想:每次遍歷一個節點,就放入到雜湊表,如果再次發現雜湊表中有這個節點的時候就是環的入口
    public ListNode EntryNodeOfLoop(ListNode pHead){
        //跑龍套節點
        ListNode dummyNode = pHead;
        //雜湊表
        HashSet<ListNode> hashSet = new HashSet<ListNode>();
        while(dummyNode!=null){
            if(hashSet.contains(dummyNode)){
                return dummyNode;  //找到入環點
            }
            hashSet.add(dummyNode);  //否則放入雜湊表
            dummyNode = dummyNode.next;
        }
        //退出迴圈,即為找到入環點,無環
        return null;
    }
}

輸出的效率:

方法2:快慢指標

思想:

總結思想:

  • 第一趟跑的時候找相遇點(定義fast=phead.next; slow = phead;錯開的好)
  • 找到相遇點計算環長
  • 重新定義快慢指標指向,pFast指向phead,pSlow指向pHead後的環長n的位置;同時以step=1走
  • 相遇即返回該節點為入環點。

時間複雜度O(n)

Java程式碼

//方法2:快慢指標(第一次快慢速度2:1找到相遇點,計算環長,第二次同步速度走,在入環點相遇)
    public ListNode EntryNodeOfLoop(ListNode pHead){
        //需要考慮只有一個節點的邊界情況
        if(pHead==null) {return null;}
        
        //快慢指標
        ListNode pFast = pHead.next; //注意一開始讓快慢指標錯開,因為不錯開的話,在第一次相遇點的邏輯中直接if比較返回相等。返回的就是首節點相遇
        ListNode pSlow = pHead;
        //標註是否有環
        boolean isCircle = false;
        //計算環長的計數器
        int count=1;
       
        //1 第一次走找是否有環和相遇點
        while(pFast!=null && pFast.next!=null){
            if(pFast==pSlow){  //在初始快慢指標賦值的時候讓錯開,不然在這裡會返回首節點相遇的
                isCircle = true;
                break;//一定記得有環了跳出while
            }
            //否則快指標2倍速走,慢指標1倍速走
            pFast = pFast.next.next;
            pSlow = pSlow.next;
        }
        //2 檢測isCircle欄位看是否有環,有環的話就計算環長
        if(!isCircle){
            return null;//無環
        }else{//有環
           //2.1 讓快指標從相遇點再繞環走一圈,計算環長
            pFast = pFast.next; 
            while(pFast!= pSlow){
                count++; //這裡while內用的dummyFast.next,故計算的count還差最後一步。
                pFast = pFast.next;
            }
            
        }
        
        //3 我們得出了環長count值,重新把指標重新指向,快慢指標以step=1開始走,相遇就是入環點
        pFast = pSlow = pHead;
        //退出while後就是找到slow應該指向的節點的地方
        while(count>0){
            pSlow = pSlow.next;
            count--;
        }
        
        while(pFast!=pSlow){
            pFast = pFast.next;
            pSlow = pSlow.next;
        }
        //退出while,返回的節點就是相遇點
        return pFast;
    }

時間複雜度 :O(n)

輸出: