首页 > 代码库 > 单链表环问题

单链表环问题

1. 判断单链表是否有环
    采用网络上常用的追赶的方式,原理:若存在环,则在环内的两个对象在速度不同时一定会相遇,速度快与速度慢的对象分别记为A,B,为避免A的一步太大,将B忽略,故设A的一步为2,B的一步为1,保证在一步内A或者在B之后,或者与B相遇;若不存在环,A先跑至终点
    
设链表为N1,N2,...,Nn
fun IsLooped :
    stepSlow = stepFast = N1;
    
    if stepFast->next == null or stepFast->next->next == null:
        return no-looped;
    stepFast = stepFast->next->next;

    while stepSlow != stepFast and stepFast->next != null and stepFast->next->next != null:
        stepSlow = stepSlow->next;
        stepFast = stepFast->next->next;
    
    if stepSlow == stepFast: // 相遇
        return looped;
    else : // 速度快的对象至终点
        return no-looped;
end fun.

2. 若单链表存在环,则求环的长度    
    上面已经能够得到相遇的位置,从相遇位置出发,只需要其中一个对象再绕环走一圈,即可得到环的长度

设链表为N1,N2,...,Nn
fun LoopLength :
    stepSlow = stepFast = N1;
    len = 0;
    
    if stepFast->next == null or stepFast->next->next == null:
        return no-looped;
    stepFast = stepFast->next->next;

    while stepSlow != stepFast and stepFast->next != null and stepFast->next->next != null:
        stepSlow = stepSlow->next;
        stepFast = stepFast->next->next;
    
    if stepSlow == stepFast: // 相遇
        len = 1; // 相遇位置即是第一个点
        stepFast = stepFast->next;
        while stepSlow != stepFast: // 没有回到相遇点,则说明该点未被统计,长度加1
            ++len;
            stepFast = stepFast->next;
        return looped, len;
    else : // 速度快的对象至终点
        return no-looped;
end fun.

3. 若单链表存在环,则求环的入口点
    将循环部分与非循环部分分开,设起始非循环部分为N1,N2,...Nm,循环部分为M1,M2,...,Mn,则单链表即为N1,N2,...,Nm,M1,M2,...Nn,M1,M2,....,目的是求点M1位置,即得到m的值.
    分析: 从上面可知,能够得到相遇位置Mi和环的长度n,额外地,可知道速度慢的对象B走过的路程S(即相遇点前面的点的个数); 由于速度快的对象A先于B到达循环部分,故在B达到点M1时,A已在循环部分溜达了t圈,并且已经在点Ms的位置,根据A走过路程有方程:

    m+tn+s = 2m     (1);

若Ms=M1,则已找到入口点,现在考虑一般情况,1<=s<=n,
    目前,对象A和B的位置分别为M1,Ms,由于A的速度是B的2倍,故A走完一圈,B仅走过半圈,故B与A第一次相遇时,B未到过Mn,故B走过的路程为

    m+(i-1) = S      (2),

A走过的路程为

    m+tn+n+(i-1) = 2S      (3)
    现在有方程 m+tn+s = 2m =>
        m = tn + s , 0<=s<n (1)
        m + (i-1) = S (2)
        m + tn + n + (i-1) = 2S (3)
    方程(3)-(2):
    S = tn + n (4),方程(1)代入(2)
    tn + s + (i-1) = S = tn + n =>
    s + (i-1) = n, => s = n - (i-1), 而Mi,Mi+1,...,Mn,M1走过的路程为n - (i-1),说明,若对象C,D分别位于Ntn+1(0<=tn<=m),Mi时,经过s步可在M1处相遇,即可得到入口。tn可由方程(4)得到

fun LoopEntry :
    stepSlow = stepFast = N1;
    len = 0;
    steps = 0;
    
    if stepFast->next == null or stepFast->next->next == null:
        return no-looped;
    stepFast = stepFast->next->next;

    while stepSlow != stepFast and stepFast->next != null and stepFast->next->next != null:
        stepSlow = stepSlow->next;
        stepFast = stepFast->next->next;
        ++steps;
    
    if stepSlow == stepFast: // 相遇
        len = 1; // 相遇位置即是第一个点
        stepFast = stepFast->next;
        while stepSlow != stepFast: // 没有回到相遇点,则说明该点未被统计,长度加1
            ++len;
            stepFast = stepFast->next;
    else : // 速度快的对象至终点
        return no-looped;
        
    // 环长度大于0,即存在环,下面一定是存在环的,因为不存在环的情况已经在上面返回了
    if len > 0 :
        tn = steps - len;
        i = 0;
        entryNode = N1;
        // entryNode移至Mtn+1位置
        while i < tn :
            entryNode = entryNode->next;
            ++i;
        // entryNode与Mi同时移动s步,与入口处相遇
        while entryNode != stepSlow :
            entryNode = entryNode->next;
            stepSlow = stepSlow->next;
        return looped, entryNode;
end fun.

单链表环问题