(Java代码实现)单链表有环的一系列问题

本文主要用Java实现关于单链表有环的5个问题:

   1. 判断一个单链表是否存在环?
   2. 若存在环,找到环的入口位置?
   3. 进一步,计算换上的节点数?
   4. 进一步,计算环外的长度、链表的长度?
   5. 环上对面的结点如何求?

解决每个问题的方法:

   1. 利用快、慢指针从表头开始往后next,慢指针去追击快指针,若遍历完返回null,就没有环;若慢指针追上快指针相遇,就有环。
   2. 经过数学推导,有这么个规律:从相遇点位置和从表头位置同时往后next,会在环的入口处相遇。
   3. 基于以上,可以有2种方法:
          (1)从环的入口处开始,在环上转一圈,计数。
          (2)从相遇点位置同时开始,快慢指针继续转,直到下次二者相遇,二者走过步数差值(或者慢指针走过的步数)就是环的长度。当然,上面的理论是建立在 快指针一次走2步,慢指针一次走一步。
   4. 基于以上,知道了入口的位置,从表头开始往入口走,计数。链表长度=环外长度+环长度。
   5. 基于这个规律:在环内,快慢指针同时出发,快指针回到原点,慢指针走到距离原点最远处。

具体详细的数学推导参见

http://blog.csdn.net/doufei_ccst/article/details/10578315


Java 代码实现如下:

public class ListNode {

    int val;
    ListNode next;

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }

    public int getVal() {
        return val;
    }

    public void setVal(int val) {
        this.val = val;
    }

    public ListNode getNext() {
        return next;
    }

    public void setNext(ListNode next) {
        this.next = next;
    }

    @Override
    public String toString() {
        return "ListNode{" +
                "val=" + val +
                '}';
    }

}


----------


public class BooleanCircular {

    private static ListNode pos;
    private static ListNode joint;


    public static void main(String[] args) {
        ListNode listNode1 = new ListNode(1);
        ListNode listNode2 = new ListNode(2);
        ListNode listNode3 = new ListNode(3);
        ListNode listNode4 = new ListNode(4);
        ListNode listNode5 = new ListNode(5);
        ListNode listNode6 = new ListNode(6);
        ListNode listNode7 = new ListNode(7);
        ListNode listNode8 = new ListNode(8);
        ListNode listNode9 = new ListNode(9);

        listNode1.setNext(listNode2);
        listNode2.setNext(listNode3);
        listNode3.setNext(listNode4);
        listNode4.setNext(listNode5);
        listNode5.setNext(listNode6);
        listNode6.setNext(listNode7);
        listNode7.setNext(listNode8);
        listNode8.setNext(listNode9);
        listNode9.setNext(listNode4);

        System.out.println(hasCycle(listNode1));
        System.out.println(FindJoint(listNode1));
        System.out.println(CircluarCount2(listNode1));
        calculateLength(listNode1);
        System.out.println(oppositeListNode(listNode4));

    }

    //判断单链表是否有环
    public static boolean hasCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;

        while (slow != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (slow == fast) {
                return true;
            }
        }
        return false;
    }

    //找到环的入口
    public static ListNode FindJoint(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;

        while (slow != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (slow == fast) {
                break;
            }
        }
        if (slow == null || fast.next == null) {
            System.out.println("没有环");
            return null;
        }

        //记录相遇位置
        pos = slow;

        ListNode node = slow;
        ListNode start = head;
        while (node != start) {
            node = node.next;
            start = start.next;
        }

        //记录环入口位置
        joint = start;

        return start;
    }

    //计算环上的节点数,两种方法
    public static int CircluarCount1(ListNode head) {
        int count = 1;
        //joint是入口位置
        ListNode node = joint.next;
        while (node != joint) {
            node = node.next;
            count++;
        }
        return count;
    }

    public static int CircluarCount2(ListNode head) {
        int count = 1;
        //pos是相遇位置
        ListNode fast = pos.next.next;
        ListNode slow = pos.next;
        while (fast != slow) {
            fast = fast.next.next;
            slow = slow.next;
            count++;
        }
        return count;
    }

    //计算头结点到环入口的长度、链表的长度
    public static void calculateLength(ListNode head) {
        int count = 0;
        //joint是入口位置
        for (ListNode node = head; node != joint; node = node.next, count++) ;
        System.out.println("头结点到入口的长度:" + count);
        int listCount = count+CircluarCount1(head);
        System.out.println("链表的长度:" + listCount);
    }

    //求环上对面的结点,这里以 listNode4 对面结点 listNode7 来验证
    public static ListNode oppositeListNode(ListNode listNode){
        ListNode slow = listNode;
        ListNode fast = listNode;

        do{
            slow=slow.next;
            fast=fast.next.next;
        }while(fast!=listNode);

        return slow;
    }
}

run结果:

true
ListNode{val=4}
6
头结点到入口的长度:3
链表的长度:9
ListNode{val=7}

本文结束,谢谢浏览!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值