[Leetcode学习]Linked List Cycle I & II(链表环)

(1)解决链表环的问题还是简单,那么怎么出现链表环的呢?

我暂时只知道JDK一个,Java1.7以及之前的HashMap,为了解决hash冲突,所以数组(table)那使用了单链表(Entry有一个next成员),但是插入链表的方法是头插法,就是插入链表头,高并发下就会造成一个链表环的问题,具体形成可以另外查询。

void addEntry(int hash, K key, V value, int bucketIndex) {
	Entry<K,V> e = table[bucketIndex];                        // 先暂存index的e
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e); // 就是这里,直接构造方法放入e
    if (size++ >= threshold)
        resize(2 * table.length);
}
static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;

        Entry(int h, K k, V v, Entry<K,V> n) {//构造方法这里是直接next赋值放入的e,是头插法
            value = v;
            next = n;
            key = k;
            hash = h;
        }
...................

后来Java8就改了,使用尾插法,而且超过8个元素,链表改成红黑树,高并发下至少不会死循环,不过会查出null。

/**
 * The bin count threshold for using a tree rather than list for a
 * bin.  Bins are converted to trees when adding an element to a
 * bin with at least this many nodes. The value must be greater
 * than 2 and should be at least 8 to mesh with assumptions in
 * tree removal about conversion back to plain bins upon
 * shrinkage.
 */
static final int TREEIFY_THRESHOLD = 8;

(2)问题 I 检验是否存在链表环

难度easy

通常解决方法也简单,就是用快慢指针(fast & slow pointer)。

输入输出样例:

Input: head = [3,2,0,-4], pos = 1 // pos是最后一个节点next指向的数组位置,如果pos = -1,那么就没有环,pos只是当作说明,不会真正输入。
Output: true // true表示验证有链表环。

我自己代码:

public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null || head.next == null) return false;
        ListNode runner01 = head; // 慢
        ListNode runner02 = head; // 快
        while(runner02.next != null && runner02.next.next != null) {
            runner01 = runner01.next;
            runner02 = runner02.next.next;
            if(runner01.val == runner02.val) return true; // 重复就存在链表环
        }
        return false;
    }
}

(3)问题 II 如果存在链表环,返回链表环的开始节点

难度easy

输入输出样例:

Input: head = [3,2,0,-4], pos = 1
Output: tail connects to node index 1 // 发现链表环开始位置为index = 1

我自己代码:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head == null || head.next == null) return null;
        ListNode runner01 = head; // 慢
        ListNode runner02 = head; // 快
        while(true) {
            // 如果快指针发现null,就代表没有链表环
            if(runner02.next == null || runner02.next.next == null) return null;
            runner01 = runner01.next;
            runner02 = runner02.next.next;
            if(runner01 == runner02) break; // 发现链表环
        }
        
        runner02 = head;    // 后续步骤是发现链表环,让快指针重新跑回开头
        while(runner01 != runner02) {
            runner01 = runner01.next;
            runner02 = runner02.next;  // 一步一步验证
        }
        return runner02;
    }
}

找到链表环开始位置的原理:

快慢指针从S开始移动,在S''相遇,可以得出

设快指针(fast)路程X1,慢指针(slow)路程X2得
X1 = m(B + C) + A + B      // 假设 快(fast) 跑了m圈
X2 = n(B + C) + A + B      // 假设 慢(slow) 跑了n圈
(1)X1 = 2 * X2           // 快 是 慢 2倍
(2)m(B + C) + A + B = 2 * (n(B + C) + A + B) // X1 和 X2 代入(1)

然后(2)相消得
A + B = (m - 2n)(B + C)
A = (m - 2n - 1)(B + C) + B + C - B // 提取一个B + C出来
A = (m - 2n - 1)(B + C) + C

而B + C是一个圆周的长度(周长),跑一圈回到开始,
所以可以理解为,慢(slow)从S''跑了 C 与重新在起点快(fast) 跑了 A 之后相遇,
所以相遇点刚好是环的开始位置

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值