『踩坑记录』寻找两个链表相交的第一个节点

LeetCode上的一道题,编写一个程序,找到两个单链表相交的起始节点。

初始解法

题目连接:两个链表相交的第一个结点
首先,我们先来看起初的解法

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA, curB = headB;
        while (curA != curB) {
            curA = curA == null ? headB : curA.next;
            curB = curB == null ? headA : curB.next;
        }
        
        return curA;
    }
}

在这里插入图片描述


解题思路
在这里插入图片描述


虽然上面的答案能够通过,但是这个答案是有问题的,因为我们没有考虑链表有环的情况,如果链表中有环的话,上面的解答就是错的。


考虑链表有环的情况

考虑链表有环,我们可以分情况进行讨论,如下
在这里插入图片描述
一共有六种情况,如上图


解题思路

  1. 首先判断两个链表是否带环,如果都不带环,按照前面判断相交的方法判断,情况1和情况2
  2. 如果一个链表带环,一个链表不带环,则两个链表不可能相交,情况4
  3. 如果两个链表都带环分别求两个环的入口点,如果入口点一样,则为情况5,如果入口点不一样,则从一个入口点出发,绕环一周,看能不能遇到另一个入口点,如果能够遇到,就是情况6,否则就是情况3

代码如下

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        boolean flagA = hasCircle(headA);
        boolean flagB = hasCircle(headB);
        
        // 两个链表都没有环
        if (flagA == false && flagB == false) {
            ListNode curA = headA, curB = headB;
            while (curA != curB) {
                curA = curA == null ? headB : curA.next;
                curB = curB == null ? headA : curB.next;
            }
            return curA;
        }
        // 一个链表有环,一个链表没有环
        else if (flagA == false || flagB == false) {
            return null;
        }
        // 两个链表都有环
        else {
            ListNode entranseA = findEntranseNode(headA);
            ListNode entranseB = findEntranseNode(headB);
            
            // 环入口结点一致
            if (entranseA == entranseB) {
                return entranseA;
            }
            // 环入口结点不一致
            else {
                HashSet<ListNode> set = new HashSet<>();
                ListNode cur = entranseA.next;
                while (cur != entranseA) {
                    // 从一个入口点出发,遇到了另一个入口点,环上相交
                    if (set.contains(entranseB)) {
                        ListNode curA = entranseA, curB = entranseB;
                        while (curA != curB) {
                            curA = curA.next;
                            curB = curB.next;
                        }
                        return curA;
                    }
                    set.add(cur);
                    cur = cur.next;
                }
                // 从一个入口点出发,没有遇到另一个入口点,不相交
                return null;
            }
        }
    }
    
    // 寻找环的入口结点
    private ListNode findEntranseNode(ListNode head) {
        ListNode fast = head, slow = head;
        while (true) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                break;
            }
        }
        
        fast = head;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        
        return slow;
    }
    
    // 判断一个链表是否有环
    private boolean hasCircle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        
        ListNode fast = head, slow = head;
        while (true) {
            if (fast == null || fast.next == null) {
                return false;
            }
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                break;
            }
        }
        return true;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值