算法学习笔记(十二)

一、算法例题
有一个单向链表,链表中可能出现环,如下图所示:

在这里插入图片描述

那么,如何用程序来判断该链表是否为环链表呢?

方法一:

首先从头节点开始,依次遍历单链表中的每一个节点。每遍历一个新节点,就从头检查新节点之前的所有节点,用新节点和此节点之前所有节点依次比较。如果发现新节点和之前的某个节点相同,则说明该节点被遍历过两次,链表有环,如果之前的所有节点中不存在与新节点相同的节点,就继续遍历下一个新节点,继续重复刚才的操作。如图中所示,当遍历到节点7的时候,从头方位节点5和节点3,发现已遍历的节点中并不存在节点7,则继续往下遍历。
当第2次遍历到节点2时,从头访问曾经遍历过的节点,发现已经遍历过节点2,说明链表有环。

假设链表的节点数量为n,则该解法的时间复杂度为O(n*n),空间复杂度为O(1).

方法二:
首先创建一个节点Id为Key的 HashSet集合,用来存储曾经遍历过的节点。然后同样从头节点开始。依次遍历单链表中的每一个节点,每遍历一个新节点,都用新节点和集合中存储的节点进行比较,如果发现集合中存在与之相同的节点ID,则说明链表有环,如果集合中不存在与之相同的节点ID,就把这个新节点ID存入集合中,之后进入下一个节点,继续重复刚才的操作。
由此可知该方法的与方法一类似,区别就是使用了集合作为额外的缓存。
假设链表的节点数量为n,则该方法的时间复杂度为o(n)。空间复杂度为O(n).

方法三:

首先创建两个指针p1和p2,让它们同时指向这个链表的头节点,然后开始一个大循环,在循环体中,让指针1每次向后移动一个节点,让指针2每次向后移动2个节点,然后比较两个指针的节点是否相同,如果相同则可以判断出链表有环,如果不同,则继续下一次循环。
理解思路:如同一个环形跑道,速度快的人迟早会第二次追上速度慢的的人。
假设链表的节点数量为N,则该算法的时间复杂度为O(n),除两个指针外,没有额外的存储空间,所以空间复杂度是O(1).

代码实现:


public class isCycle {
    /**
     * 判断是否有环
     * @param head
     * @return
     */
    public static boolean isCycle(Node head){
        Node p1 = head;
        Node p2 = head;
        while (p2!=null &&p2.next !=null){
            p1 = p1.next;
            p2 = p2.next.next;
            if (p1==p2){
                return true;
            }
        }
    return false;
    }
    private static  class Node{
        int data;
        Node next;
        Node(int data){
            this.data = data;

        }
    }
    public static void main(String[] args) throws  Exception{
        Node node1 = new Node(5);
        Node node2 = new Node(3);
        Node node3 = new Node(7);
        Node node4 = new Node(2);
        Node node5 = new Node(6);
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        node5.next=node2;
        System.out.println(isCycle(node1));

    }
}

结果:

在这里插入图片描述

二、问题扩展

1、如果链表有环,求出环的长度?
2、如果链表有环,如何求出入环节点?

当两个指针首次相遇,证明链表有环的时候,让两个指针从相遇点继续循环前进,并统计前进的循环次数,直到两个指针第二次相遇。此时,统计出来的前进次数就是环长。
因为指针1每次走一步,指针2每次走两步,两者的速度差是一步,当两个指针再次相遇时,2比1多走了一圈。因此:

	环长=每一次速度差*前进次数=前进次数

假设从链表头节点到入环点的距离时D,从入环点到两个指针首次相遇的距离是S1,从首次相遇点回到入环点的距离是S2.
那么,当两个指针首次相遇时,各自所走的距离时多少呢?
指针1一次只走一步,所走的距离是D+S1.
指针2一次走2步,多走了n(n>-1)整圈,所走的距离是D+S1+N(s1+s2)

由于指针2的速度是指针1的2倍,所以所走的距离也是指针1 的2倍,因此:

		2(D+S1)=D+S1+N(S1+S2)

等式经过整理得出:

		D=(N-1)(S1+S2)+S2

也就是说,从链表头节点到入环点的距离,等于从首次相遇点绕环n-1圈再回到入环点的距离。
只要把其中一个指针放回到头节点位置,另一个指针保持再首次相遇点,两个指针都是每次向前走一步,那么,它们最终相遇的节点就是入环点。
头大大

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值