每日一道Leetcode算法——如何判断一个链表是否有环,并求出环的入口和环的长度——

判断一个链表是否有环有两种办法:

一种最经典是定义两个指针,一个指针每次向前走一步,一个指针每次向前走两步,如果两个指针最终重合。则证明有环。

一种是创建一个hash表,将每次走过的结点放入hash表中,如果结点在hash表中,则表示存在环。

判断链表的入口:

假设两个指针第一次相遇点为m,此时令一个指针从头结点向下走,每次走一步,令另一个指针从相遇点往下走,每次走一步,两个指针相遇的位置,为入口处。

判断环的长度:

设置一个指针q指向环的入口,让q往后移动直到q再次等于环的入口结点,此时q所走的步数就是环的长度。

package cn.leetcode.linkedlist;

import cn.kimtian.linkedlist.Node;

import java.util.HashMap;

/**
 * 1.如何判断一个链表是否有环
 * 2.如果有环,找到环入口
 * 3.如果有环,求环的长度
 *
 * @author kimtian
 */
public class LinkLoop {
    public static void main(String[] args) {
        //创建一个链表,为A->B->C->D->E->B
        Node nA = new Node(1);
        Node nB = new Node(2);
        Node nC = new Node(3);
        Node nD = new Node(4);
        Node nE = new Node(5);
        nA.next = nB;
        nB.next = nC;
        nC.next = nD;
        nD.next = nE;
        //这句使其成为一个带环的链表
        nE.next = nB;
        System.out.println(hasLoop(nA));
        System.out.println(searchEntranceNode(nA).getData());
        System.out.println(circleLength(nA));
    }

    /**
     * 方法一:创建两个指针,一个走一步,一个走两步,如果两个指针最后重合,说明这个链表有环
     *
     * @param n 链表头结点
     * @return 是否有环
     */
    public static boolean hasLoop(Node n) {
        //单链表为空时,单链表没有环
        if (n == null) {
            return false;
        }
        //单链表中只有头结点,而且头结点的next为空,单链表没有环
        if (n.next == null) {
            return false;
        }
        //定义两个指针tmp1,tmp2,一个走一步,一个走两部
        Node tmp1 = n.next;
        Node tmp2 = n.next.next;

        while (tmp2 != null) {
            if (tmp1.getData() == tmp2.getData()) {
                //如果两个指针最后重逢,说明存在环,否则不存在
                return true;
            }
            //每次迭代指针一走一步,指针二走两步
            tmp1 = tmp1.next;
            //如果下一个都为空了,就不要找下下一个的值了,防止空指针异常
            if (tmp2.next != null) {
                tmp2 = tmp2.next.next;
            }
            //没有下一个指向了,说明不是环
            else {
                return false;
            }
        }
        return false;
    }

    /**
     * 方法二:将每次走过的结点保存到hash表中,如果结点在hash表中,则表示存在环
     *
     * @param n 链表头结点
     * @return 是否有环
     */
    public static boolean hasLoop2(Node n) {
        Node temp1 = n;
        HashMap<Node, Node> ns = new HashMap<Node, Node>();
        while (n != null) {
            if (ns.get(temp1) != null) {
                return true;
            } else {
                ns.put(temp1, temp1);
            }
            temp1 = temp1.next;
            if (temp1 == null) {
                return false;
            }
        }
        return true;
    }

    /**
     * 如果有环,求环的入口结点
     *
     * @param n 链表头结点
     * @return 入口结点
     */
    public static Node searchEntranceNode(Node n) {
        //单链表为空时,单链表没有环
        if (n == null) {
            return null;
        }
        //单链表中只有头结点,而且头结点的next为空,单链表没有环
        if (n.next == null) {
            return null;
        }
        //定义两个指针tmp1,tmp2,一个走一步,一个走两部
        Node tmp1 = n.next;
        Node tmp2 = n.next.next;

        while (tmp2 != null) {
            if (tmp1.getData() == tmp2.getData()) {
                //如果两个指针最后重逢,说明存在环,否则不存在
                break;
            }
            //每次迭代指针一走一步,指针二走两步
            tmp1 = tmp1.next;
            //如果下一个都为空了,就不要找下下一个的值了,防止空指针异常
            if (tmp2.next != null) {
                tmp2 = tmp2.next.next;
            }
            //没有下一个指向了,说明不是环
            else {
                return null;
            }
        }
        //这里直接等于头结点,没有向下走一步,因为头结点有可能就是环的入口结点
        tmp2 = n;
        //假设两个指针第一次相遇点为m,此时令一个指针从头结点向下走,每次走一步,令另一个指针从相遇点往下走,每次走一步,两个指针相遇的位置,为入口处
        while (tmp2 != null) {
            if (tmp1.getData() == tmp2.getData()) {
                return tmp1;
            }
            tmp1 = tmp1.next;
            tmp2 = tmp2.next;
        }

        return null;
    }

    /**
     * 如果有环,求环的长度
     * 设置一个指针q指向环的入口,让q往后移动直到q再次等于环的入口结点,此时q所走的步数就是环的长度
     *
     * @param n 链表头结点
     * @return 入口结点
     */
    public static int circleLength(Node n) {
        Node p = searchEntranceNode(n);
        //不存在环时,返回0
        if (p == null) {
            return 0;
        }
        Node q = p.next;
        int length = 1;
        while (p != q) {
            length++;
            q = q.next;
        }
        //返回环的长度
        return length;
    }
}

猜你喜欢

转载自blog.csdn.net/third_/article/details/86307712