链表有环的判断

问题

今日一问:我们知道,在一个链表里,如果某一个节点的next属性指向了链表的另一个节点,那么这个链表就是有环的。 那么今天的问题是:如何判断一个单链表是有环的。一个有环的链表会给程序带来什么样的隐患。

提示:你可能会想到在链表里选取一个节点,然后对链表进行循环,如果循环到了这个节点就说明链表有环了,但是这个办法的问题是如何确保选取的节点在链表里,如果有办法确定这点,实际上就相当于判断了链表是否有环,问题又绕回来了。

解答

这个问题实际有固定的解答方式,就是使用快慢指针的方式。使用两个指向链表头的引用,然后循环迭代,一个指针每次往后移到一位,一个指针每次往后移动两位,那么只会有两种情况:

  1. 链表无环,快指针移到next为空的地方;
  2. 链表有环,连个指针必相遇;

第一种情况毫无疑问,对于第二种情况,慢指针总会移动到链表的环上,然后快指针总会追上慢指针,且快指针每次走两步,不会“越过”慢指针所以终会相遇。

public class Test {
    public static void main(String[] args) {

        IntegerNode list = new IntegerNode(0);
        IntegerNode prep = list;
        IntegerNode ringStart = null;
        for (int i = 1; i < 100; i++) {
            IntegerNode node = new IntegerNode(i);
            prep.next = node;
            prep = node;
            if (i == 93) {
                ringStart = node;
            }
            if (i == 99) {
                node.next = ringStart;
            }
        }
        System.out.println(hasRing(list));


    }

    private static boolean hasRing(IntegerNode list) {
        boolean result = false;
        IntegerNode one = list;
        IntegerNode two = list;
        IntegerNode meetingPoint = null;

        while (two != null) {
            one = (IntegerNode) one.next;
            two = (IntegerNode) two.next;
            if (two != null) {
                two = (IntegerNode) two.next;
            }
            if (one == two) {
                result = true;
                meetingPoint = one;
                System.out.println("交汇点:" + one.getValue());
                break;
            }
        }
        return result;
    }
}

如上是判断链表是否有环的代码。

那么知道链表有环之后,还有如下需要计算:

  1. 哪一个Node是环的起始位置(也就是从链表的头部往后迭代,遇到的第一个在环里的Node);
  2. 环的长度;

对于如上两个问题,我们先记如下几个值:

  1. 链表的长度(也就是Node的个数)为length;
  2. 不在环里的Node个数为a;
  3. 在环里的Node个数为b;
  4. 交汇点是环里的第h个Node;

那么有如下等式:

2(a+h-1)=a+n*b+h-1

其中n是第一次相遇时,快指针所走的环的圈数。

可以得出:

a+h-1=n*b

也就是第一次相遇时,慢指针走的步数是环长的整数倍。

如果在他们相遇后,一个指针从相遇点开始,一个从链表头部开始,每次走一步,那么当从链表头开始走的走到环的起始位置时,走了a-1步,另一个指针也走a-1步,也就是nb-h步,二从环的第h位走nb-h正好也是走到环的起始位置,所以判断环的启示位置以及的方法如下:

//计算环的第一个点
if (result) {
    one = meetingPoint;
    two = list;
    while (one != two) {
        one = (IntegerNode) one.next;
        two = (IntegerNode) two.next;
    }
    System.out.println("环的第一个点:" + one.getValue());
    int count = 1;
    two = (IntegerNode) two.next;
    while (one != two) {
        two = (IntegerNode) two.next;
        count++;
    }
    System.out.println("环的长度是" + count);

}

至于链表有环给程序带来的隐患,就很简单了,如果我们对列表做循环时没有判断这一点,那么就会出现死循环。

发布了19 篇原创文章 · 获赞 8 · 访问量 4040

猜你喜欢

转载自blog.csdn.net/u014068277/article/details/103951222