Java单链表基本操作(十)--判断单链表是否有环并输出环长度以及环的入口节点

本文解决三个问题:

1.单链表是否有环? 
2.有则输出环的长度? 
3.找到环的入口节点?

分析: 
定义两个指针fast 和slow,fast每次向后移动两个节点,slow每次想后移动一个节点。 
1.如果没有环,则fast首先到达链表结尾; 
2.链表有环的情况下:fast与slow两次相遇,slow中间走过的节点处即为环的长度; 
3.找环的入口节点稍微复杂点,有如下的推导过程:

相遇的时候,slow共移动了s步,fast共移动了2s步。 
定义a如下: 链表头移动a步到达入口点。 
定义x如下: 入口点移动x步到达相遇点。 
定义r如下: 环的长度。 
定义L如下: 链表总长度为L。

其中L = a + r

那么slow和fast相遇了,fast必然比slow多走了n个圈,也就是 n*r 步,那么 
s = a + x 
2s = s + n*r , 可得 s = n*r 
将s=a+x,带入s =n*r,可得 a+x = n*r, 也就是 a+x = (n-1)*r + r    
从表头移动到入口点,再从入口点移动到相遇点,也就是移动了整个链表的距离,即是 L = a + r , 所以r = L - a 
所以 a+x = (n-1)*r + L - a , 于是 a = (n-1)*r + L - a - x 
得到:从表头到入口点的距离,等于从相遇点到入口点的距离。 
所以,从表头设立一个指针,从相遇点设立一个指针,两个同时移动,必然能够在入口点相遇,这样,就求出了相遇点。

上面三个问题的java解决代码:

方法一:一次性求解

public class ExistCircle {
    static int id = 1;
    public Node existCircle(Node head){
        Node fast = head;
        Node slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;

            //输出环长
            while(fast == slow) {
                int len = 1;
                fast = fast.next.next;
                slow = slow.next;
                while (fast != slow) {
                    len++;
                    fast = fast.next.next;
                    slow = slow.next;
                }
                System.out.println("The length of circle is:" + len);

                //输出环入口节点
                fast = head;
                while (fast != slow) {
                    fast = fast.next;
                    slow = slow.next;
                    id++;
                }
                return slow;
            }
        }
        return null;
    }
    public static void main(String[] args) {
        Node head=new Node(3);
        Node node1=new Node(6);
        Node node2=new Node(8);
        Node node3=new Node(5);
        Node node4=new Node(2);
        Node node5=new Node(7);
        head.next=node1;
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        node5.next=node3;
        Node port = new ExistCircle().existCircle(head);
        System.out.println("环入口为第" + id + "个节点:" + port.data);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

方法二:分开求解:

package listnode;
/**
 * @author Gavenyeah
 * @date Time: 2016年5月18日上午11:36:40
 * @des: 
 */
public class ExitCircle {
    static int id = 1;

    public static void main(String[] args) {
        //测试
        Node head=new Node(3);
        Node node1=new Node(6);
        Node node2=new Node(8);
        Node node3=new Node(5);
        Node node4=new Node(2);
        Node node5=new Node(7);
        head.next=node1;
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        node5.next=node3;
        new ExitCircle().exitCircle(head);

        Node port = new ExitCircle().findLoopPort(head);
        System.out.println("环入口为第" + id + "个节点:" + port.data);

    }
    //环入口节点
    //环的入口节点到快慢指针相遇的距离 与 链表头节点到环入口节点的距离相等
    public Node findLoopPort(Node head){
        Node slow = head, fast = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){
                fast = head;
                while(head != slow){
                    id++;
                    head = head.next;
                    slow = slow.next;
                }
                return slow;
            }
        }
        System.out.print("NoLoop !");
        return null;
    }

    public boolean exitCircle(Node head){
        Node fast = head;
        Node slow = head;
        while(fast != null && fast.next != null){//判断是否由环,注意fast.next = null的情况
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){//存在环
                int count = 0;
                while(true){
                    count ++;
                    fast = fast.next.next;
                    slow = slow.next;

                    if(fast == slow){//快慢指针在第二次相遇,这个点肯定是第一次相遇的点 
                        System.out.println("环的长度:" + count);
                        return true;
                    }
                }
            }
        }
        System.out.println("false!");
        return false;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/y999666/article/details/51444504




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值