关于链表的一些操作——判断是否有环、寻找入口点等

一、提纲

0、单向链表的翻转
1、判断链表是否有环
2、寻找环的入口点
3、计算环的节点数
4、计算(有环)链表的节点数
5、找出环中距任意一点最远的节点
6、判断两个无环链表是否相交
7、寻找两个链表的相交的节点

二、核心算法

以上问题的核心算法都是利用“快慢指针
快指针:步长为2
慢指针:步长为1
快慢指针一开始都指向链表头,同时遍历链表,若链表有环,快慢指针必然会在环中相遇,而且是在慢指针未走完一圈时相遇。若存在相遇点,则说明链表有环;若不存在,则没有环。
之后的问题都是直接或间接通过相遇点求得答案。

三、核心代码

    /**
    * @Title: findMeetingNode
    * @Description: 寻找链表中环的相遇节点(若有环,快慢指针必会在环中相遇)
    * @param head 链表头
    * @return   有环返回相遇节点,无环返回null
    * @return Node    返回类型
    * @throws
    * 
    * 通过快慢指针判断链表是否有环
    *若有环,必会在环内相遇,且是slow未走完一圈时相遇
    */ 
    private static Node findMeetingNode(Node head){

        Node fast = head;
        Node slow = head;
        while (fast != null && slow != null) {

            if (fast.next != null) {
                fast = fast.next.next;
            } else {
                //fast.next为null,说明无环,直接返回null
                return null;
            }

            slow = slow.next;
            if (fast == slow) {
                // 若相等说明有环,返回相遇节点
                return fast;
            }
        }
        //若无环,说明fast为null
        return fast;
    }

四、关键代码

1、判断链表是否有环

/**
    * @Title: exitLoop
    * @Description: 通过findMeetingNode的返回节点判断是否有环
    *                           若返回节点不为空,必然有环,反之亦然
    * @param head 链表头
    * @return   有环返回true,否则返回false
    * @return boolean    返回类型
    * @throws
    */ 
    public static boolean existLoop(Node head) {
        //寻找环中相遇点
        Node meet = findMeetingNode(head);

        return meet != null ? true : false;

    }

2、寻找环的入口点

    /**
    * @Title: findLoopStartNode
    * @Description: 寻找环的入口点,找到则返回入口点,找不到则返回null
    * @param head 链表头
    * @return   入口节点或null
    * @return Node    返回类型
    * @throws
    * 若单向链表中环,通过快慢指针找到在环中相遇的节点meet
    * 令一个指针指向链表头,一个指针指向meet,步长为1往前走
    * 两指针必会在环的入口点相遇
    * 
    */ 
    public static Node findLoopStartNode(Node head){
        //寻找环中相遇点
        Node meet = findMeetingNode(head);
        if (meet!=null) {
            Node point1=head;
            Node point2=meet;
            while (point1!=point2) {
                point1=point1.next;
                point2=point2.next;
            }
            //相遇点便是入口点
            return point1;
        } else {
            return null;
        }
    }

3、计算环的节点数

    /**
    * @Title: countLoop
    * @Description: 统计环中节点个数。
    * @param head 链表头
    * @return   环中节点个数
    * @return int    返回类型
    * @throws
    * 找到环中相遇节点后,令指针从相遇节点走一圈,统计走过的节点数
    * 
    */ 
    public static int countLoop(Node head){
        //找出环中相遇节点
        Node meet = findLoopStartNode(head);
        if (meet!=null) {
            Node point=meet.next;
            int count=1;
            while (point!=meet) {
                point=point.next;
                count++;
            }
            return count;
        } else {
            //若节点为null则说明无环,返回0
            return 0;
        }
    }

4、计算(有环)链表的节点数

    /**
    * @Title: countLinkedList
    * @Description: 统计链表的节点个数(无论是否有环)
    * @param head 链表头
    * @return   链表的节点个数
    * @return int    返回类型
    * @throws
    * 
    * 环的节点个数+链表头到环入口点节点个数=链表节点个数
    */ 
    public static int countLinkedList(Node head){

        //找出环的长度
        int loop=countLoop(head);

        //找出环的入口点
        Node startNode = findLoopStartNode(head);
        int head2start=0;
        Node point=head;
        //计算链表头到入口点长度
        while (point!=startNode) {
            point=point.next;
            head2start++;
        }

        //环的长度+链表头到环入口点长度即为链表长度
        return loop+head2start;
    }

5、找出环中距任意一点最远的节点

    /**
    * @Title: findTheFarthestNodeInLoop
    * @Description: 指定环中任意一个节点,在环中寻找离该节点最远的一点
    * @param theNode 任意一节点
    * @return   存在这样一个节点则返回该节点,若不存在返回null
    * @return Node    返回类型
    * @throws  
    * 环中距任一点最远的点,即是从该点出发走半圈到达的点
    * 
    */ 
    public static Node findTheFarthestNodeInLoop(Node theNode){
        //判断是否存在环
        if (!existLoop(theNode)) {
            //不存在环直接返回null
            return null;
        }
        Node fast = theNode;
        Node slow = theNode;
        while (fast != theNode && fast != theNode.next) {
            fast = fast.next.next;
            slow = slow.next;
        }
        if (fast == null) {
            return fast;
        } else {
            return slow;
        }

    }

6、判断两个无环链表是否相交

    /**
    * @Title: existIntersect
    * @Description: 判断两个无环链表是否相交
    * @param listA 链表A
    * @param listB 链表B
    * @return   相交返回true,不相交返回false
    * @return boolean    返回类型
    * @throws
    * 
    * 判断两个无环链表是否相交
    * 将其中一个链表(链表A)的尾指针指向链表头,判断另一个链表(链表B)是否有环,
    * 此时,链表A是肯定有环的;若两链表不相交,链表B是肯定没有环的
    * 若链表B有环,两个链表必然相交
    */ 
    public static boolean isIntersect(MyLinkedList listA,MyLinkedList listB){
        //将A链表的尾指向链表头
        listA.getLast().setNext(listA.getHead());

        //判断链表B是否有环
        boolean intersection=existLoop(listB.getHead());

        //将链表A还原
        listA.getLast().setNext(null);

        //有环说明相交,无环说明不相交
        return intersection;
    }

7、寻找两个链表的相交的节点

/**
    * @Title: findIntersection
    * @Description: 找出两链表的相交点
    * @param listA 链表A
    * @param listB 链表B
    * @return   若有相交点则返回该节点,否则返回null
    * @return Node    返回类型
    * @throws
    */ 
    public static Node findIntersection(MyLinkedList listA,MyLinkedList listB){
        //将A链表的尾指向链表头
        listA.getLast().setNext(listA.getHead());

        //找出环的入口点,入口点既是相交点
        Node Intersection = findLoopStartNode(listB.getHead());

        //将链表A还原
        listA.getLast().setNext(null);

        return Intersection;
    }

上面只给了关键代码,需要完整代码的读者可以在这里下载(由于规则限制,无法设置下载积分为0分)

如果觉得上面的代码说的不清楚,请点击下面的链接查看。

参考:判断链表中是否有环 —– 有关单链表中环的问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值