美文网首页
链表中环的检测

链表中环的检测

作者: 云飞扬1 | 来源:发表于2020-02-21 14:17 被阅读0次

    1. 怎么检测一个单向链表中是否有环

    同样借助于快慢指针,思路如下:

    1. 定义 fast、slow 两个指针同时指向链表头部;
    2. 开始遍历链表,slow 每次移动步数为 1 ,fast 每次移动步数为 2 ;
    3. 如果链表中没有环,则 fast 指针必然会移动到链表尾部,最后会指向 null;
    4. 如果链表中有环,则在移动的过程中,fast 与 slow 两个指针必然会在某个节点相遇;

    想象成环形运动场跑步,跑得慢的人与跑得快的人同时出发,跑得快的人必然会超过跑得慢的人,并且会再次追上。

    代码如下(kotlin编写):

    /**
     * 链表数据结构
     */
    class Node constructor(val data: Char) {
        var next: Node? = null
    
        override fun toString(): String {
            val sb = StringBuilder()
            sb.append(data)
            var tmp: Node? = next
            while (tmp != null) {
                sb.append(tmp.data)
                tmp = tmp.next
            }
            return sb.toString()
        }
    }
    
    /**
     * 检测是否有环
     */
    fun detectCircle(head: Node?): Boolean {
        head ?: return false
        head?.next ?: return false
    
        var fast = head
        var slow = head
        while (fast?.next != null) {
            slow = slow?.next
            fast = fast?.next?.next
            if (fast == slow) {
                return true
            }
        }
        return false
    }
    
    //测试代码
    fun main() {
        var node1 = Node('1')
        var node2 = Node('2')
        var node3 = Node('3')
        var node4 = Node('4')
        var node5 = Node('5')
        var node6 = Node('6')
        node1.next = node2
        node2.next = node3
        node3.next = node4
        node4.next = node5
        node5.next = node6
        node6.next = node3
    
        println(detectCircle(node1))
    }
    

    2. 找到链表环的入口节点

    image.png

    图中入口节点为 3 ,由于 x 与 z 相等,所以我们用 2 个指针,一个从链表头部 1,一个从相遇点 6, 同时开始同速度移动,最后它俩的相遇点就是环的入口节点。

    这个图比较简单,只是方便理解。实际上,x 可能会很大,而链表内的环长度比较小,并不能照图中来推论,下面是具体推论:

    首先根据图中所示,需要求解 x 的长度?
    环长:L = y + z
    slow指针移动长度:S = x + y
    fast指针移动长度:2S = x + nL + y(fast 是 slow 的 2 倍速度,n 目前未知)
    所以: 2(x + y) = x + nL + y
    求得:x = nL - y = nL - y - z + z = nL - (y + z) + z = (n - 1)L + z
    最终:x = (n - 1)L + z,这里 n >= 1
    
    假设一个指针从链表头部,一个指针从环里的相遇点开始移动,当前者移动到环入口节点时:
    当 n = 1 ,从相遇点到环入口节点只需移动 z 次;
    当 n = 2 ,从相遇点到环入口节点,需要绕着环走一圈后,再移动 z 次;
    以此类推,不管怎么样,这2个指针最终都会在入口处相遇。
    

    代码如下:

    fun findCircleEntryNode(head: Node?): Node? {
        head ?: return null
        head?.next ?: return null
    
        var fast = head
        var slow = head
        while (fast?.next != null) {
            slow = slow?.next
            fast = fast?.next?.next
            if (fast == slow) {
                break
            }
        }
        //如果有环
        if (fast == slow) {
            if (fast == head)
                return head
            var tmp = head
            while (tmp?.next != null) {
                tmp = tmp?.next
                fast = fast?.next
                if (tmp == fast) {
                    return tmp
                }
            }
        }
        return null
    }
    

    时间复杂度:O(n)
    空间复杂度:O(1)

    相关文章

      网友评论

          本文标题:链表中环的检测

          本文链接:https://www.haomeiwen.com/subject/wfwfqhtx.html