• 面试题:如何判断单向链表中是否有“环”


    题目

    有一个单向链表,链表中有可能出现“环”,就像下图这样。那么,如何用程序来判断该链表是否为有环链表呢?

    解决方案

    方法1:

    首先创建一个以节点ID为Key的HashSet集合,用来存储曾经遍历过的节点。然后同样从头节点开始,依次遍历单链表中的每一个节点。每遍历一个新节点,都用新节点和HashSet集合中存储的节点进行比较,如果发现HashSet中存在与之相同的节点ID,则说明链表有环,如果HashSet中不存在与新节点相同的节点ID,就把这个新节点ID存入HashSet中,之后进入下一节点,继续重复刚才的操作。
    遍历过5、3。
    遍历过5、3、7、2、6、8、1。

    当再一次遍历节点2时,查找HashSet,发现节点已存在。

    由此可知,链表有环。 
    假设链表的节点数量为n,则该解法的时间复杂度是O(n)。由于使用了额外的存储空间,所以算法的空间复杂度同样是O(n)。

    方法2:

    首先创建两个指针p1和p2(在Java里就是两个对象引用),让它们同时指向这个链表的头节点。然后开始一个大循环,在循环体中,让指针p1每次向后移动1个节点,让指针p2每次向后移动2个节点,然后比较两个指针指向的节点是否相同。如果相同,则可以判断出链表有环,如果不同,则继续下一次循环。
    第1步,p1和p2都指向节点5。 

    第2步,p1指向节点3,p2指向节点7。

    第3步,p1指向节点7,p2指向节点6。 

    第4步,p1指向节点2,p2指向节点1。 

    第5步,p1指向节点6,p2也指向节点6,p1和p2所指相同,说明链表有环。

    分析: 

    此方法就类似于一个追及问题。在一个环形跑道上,两个运动员从同一地点起跑,一个运动员速度快,另一个运动员速度慢。当两人跑了一段时间后,速度快的运动员必然会再次追上并超过速度慢的运动员,原因很简单,因为跑道是环形的。假设链表的节点数量为n,则该算法的时间复杂度为O(n)。除两个指针外,没有使用任何额外的存储空间,所以空间复杂度是O(1)。

    代码:

    package arithmetic.com.ty.binary;
    
    public class LinkedCycle {
        /**
         * 判断是否有环
         * 
         * @param head 链表头节点
         */
        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));
        }
    }

    扩展问题

    如果单向链表中存在环的情况,环长是多少?

    思路:当两个指针首次相遇,证明链表有环的时候,让两个指针从相遇点继续循环前进,并统计前进的循环次数,直到两个指针第2次相遇。此时,统计出来的前进次数就是环长。因为指针p1每次走1步,指针p2每次走2步,两者的速度差是1步。当两个指针再次相遇时,p2比p1多走了整整1圈。因此,环长 = 每一次速度差 × 前进次数 = 前进次数。 
    代码:
        /**
         * 计算环长
         */
        public static int cycleLenth(Node head) {
            Node p1 = head;
            Node p2 = head;
            int count = 0;
            int length = 0;
            
            while (count < 2 && p2 != null && p2.next != null) {
                if(count > 0) {
                    length++;
                }
                p1 = p1.next;
                p2 = p2.next.next;
                if (p1 == p2) {
                    count++;
                }
            }
            
            return length;
        }
  • 相关阅读:
    算法竞赛入门经典训练指南——UVA 11300 preading the Wealth
    hiho一下 第148周
    ajax总结及案例
    Spring事务
    Struts2拦截器介绍
    Struts2的拦截器----Dog实例
    Struts2文件的下载
    Struts2文件的上传
    Struts2类型转换
    Struts2 属性驱动、模型驱动、异常机制
  • 原文地址:https://www.cnblogs.com/alimayun/p/12781501.html
Copyright © 2020-2023  润新知