算法系列之-两个链表是否相交

链表交点检测
本文介绍了一种判断两个链表是否相交的方法,并提供了三种情况的解决方案:两个链表都没有环、两个链表都有环以及一个有环一个无环的情况。通过使用快慢指针技巧,有效地找到了链表相交的第一个节点。

package it.list;

/**
* @项目名称:util
* @类名称:Huan @类描述:判断两个链表是否有交点
*
* @author 赵建银
* @date 2018年1月23日
* @time 下午8:05:19
* @version 1.0
*/
public class Huan {

/**
 * @param head
 * @return 获取有环链表的入环节点
 */
public static Node getLoopNode(Node head) {
    if (head == null || head.next == null || head.next.next == null) {
        return null;
    }

    Node slow = head.next;// 慢指针
    Node fast = head.next.next;// 快指针
    while (slow != fast) {
        if (slow.next == null || fast.next == null) {
            return null;
        }
        slow = slow.next;
        fast = fast.next.next;
    } // 两个相交
        // 快指针从头开始
    fast = head;
    while (fast != slow) {
        fast = fast.next;
        slow = slow.next;
    } // 和慢指针在次相交,极为入环的第一个节点
    return fast;
}

/**
 * 
 * 两个链表都没有环
 * 
 * @param head1
 * @param head2
 * @return
 */
public static Node NoLoopNode(Node head1, Node head2) {
    int n = 0;
    Node cur1 = head1;
    Node cur2 = head2;
    while (cur1.next != null) {
        n++;
        cur1 = cur1.next;
    }
    // 记录1尾节点
    while (cur2.next != null) {
        n--;
        cur2 = cur2.next;
    }
    // 记录2尾节点
    if (cur1 != cur2) {
        return null;
    }
    cur1 = n > 0 ? head1 : head2;
    cur2 = cur1 == head1 ? head2 : head1;
    n = Math.abs(n);
    // 长的链表先走n步,之后一起走
    while (n != 0) {
        n--;
        cur1 = cur1.next;
    }
    // 相交的为第一个节点
    while (cur1 != cur2) {
        cur1 = cur1.next;
        cur2 = cur2.next;
    }
    return cur1;
}

/**
 * 两个链表都有环
 * 
 * @param head1
 * @param loop1
 *            入环节点
 * @param head2
 * @param loop2
 * @return
 */
public static Node BothLoop(Node head1, Node loop1, Node head2, Node loop2) {
    Node cur1 = head1;
    Node cur2 = head2;
    if (loop1 != loop2) {// 入环节点不同
        cur1 = loop1.next;
        cur2 = loop2;
        while (cur1 != loop1) {// 第一个绕环一周
            if (cur1 == cur2) {// 发现第二个
                return loop1;// 返回任意一个
            }
            cur1 = cur1.next;
        }
        // 没有说明各自是一个环,没有焦点
        return null;
    } else {
        // 如环节点相同,在入环之前,相当于无环链表
        int n = 0;
        while (cur1.next != loop1) {
            n++;
            cur1 = cur1.next;
        }
        while (cur2.next != loop2) {
            n--;
            cur2 = cur2.next;
        }
        if (cur1 != cur2) {
            return null;
        }
        cur1 = n > 0 ? head1 : head2;
        cur2 = cur1 == head1 ? head2 : head1;
        n = Math.abs(n);
        while (n != 0) {
            n--;
            cur1 = cur1.next;
        }
        while (cur1 != cur2) {
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;

    }
}

/**
 * 
 * 
 * @param head1
 * @param head2
 * @return 两个链表相交的第一个节点
 */
public static Node GetNode(Node head1, Node head2) {
    if (head1 == null || head2 == null) {
        return null;
    }
    Node loopNode = getLoopNode(head1);
    Node loopNode2 = getLoopNode(head2);
    Node res = null;
    if (loopNode != null && loopNode2 != null) {// 都有环
        res = BothLoop(head1, loopNode, head2, loopNode2);
    }
    if (loopNode == null && loopNode2 == null) {// 都没环
        res = NoLoopNode(head1, head2);
    }
    return res;
}

}

判断两个链表是否相交的问题通常可以使用快慢指针(Floyd算法也称为龟兔赛跑法)来解决。以下是基本的步骤: 1. **创建两个指针**:分别初始化为两个链表的头节点,记为`p1`和`q1`。 2. **设置两个慢速指针**:让它们都向链表的末尾移动,速度分别为每一步走一格(`p1`)和两格(`q1`)。 3. **当一个指针到达链表末尾时**,检查另一个指针的位置。如果`q1`已经到达了`p1`的位置,说明两个链表有交叉点;如果`q1`还没到达`p1`的终点,则将`q1`恢复到初始位置(即从链表的头节点开始),然后让`q1`继续以两步一格的速度前进,同时`p1`保持原地不动。 4. **比较当前指针**:如果`q1`再次追上`p1`,那么它们之前的交叉点就是两个链表相交的地方。如果没有交叉点,就表示这两个链表相交。 ```c // 假设链表节点结构为 struct ListNode { // int val; // struct ListNode* next; // }; struct ListNode* intersect(struct ListNode* p1, struct ListNode* q1) { struct ListNode *slow_p = p1, *fast_p = q1; while (slow_p && fast_p) { if (slow_p->next) { slow_p = slow_p->next; if (fast_p->next) { fast_p = fast_p->next->next; } else { // 如果 fast_p 的下一个节点为空,说明 q1 已经越过 p1 fast_p = p1; } } else { // slow_p 走完一圈回到起点,此时与 fast_p 比较 slow_p = q1; } if (slow_p == fast_p) { return slow_p; } } return NULL; // 链表没有相交 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值