单链表相交问题

问题描述:

  如果两个链表相交,返回交点;否则,返回NULL。

算法思路

  1、如果两个链表相交,那么从交点开始往后走,两个链表的所有节点都应该相同,那么最后一个节点也必然相同,所以先找到最后一个节点,判断是否相同,从而确定链表是否相交。

     1.1、如果最后一个节点相同,那么从它往前找到第1次出现相同节点的位置,则找到了交点。但是由于单链表无法往前遍历,所以需要考虑两个链表的长度是否相同,如果链表长度相同,从前往后找;如果长度不同,则先移动长链表的链表头,使之与短链表长度相同,即两个链表对齐,然后再从前往后找,第1次遇到相同的节点即为交点。

  2、如果链表有环,那么遍历链表找最后一个节点会陷入死循环,所以需要先判断链表是否有环。如果链表有环,而又要求两个链表相交,那么它们的环一定相同,但是两个链表入环的节点可能相同,也可能不同。

     2.1、如果入环节点相同:那么从入环节点往前找交点,交点也可能就是入环节点;找交点的方式同1.1。

     2.2、如果入环节点不同:那么有可能两个链表不相交,也可能只是两个链表的入环位置不同。所以使用其中一个链表的入环节点,沿着环遍历一圈,如果可以遇到第2个链表的入环节点,那么它们相交,否则不相交。

typedef struct node {
    struct node *next;
    int value;
} node_t;

static node_t*
get_loop_node(node_t *list)
{
    node_t *fast = list;
    node_t *slow = list;
    
    while (fast && fast->next) {
        // 快慢指针都是从第1个节点开始, 开始是相等的, 所以先移动后判断
        fast = fast->next->next;
        slow = slow->next;
        
        // 首次相遇
        if (fast == slow) {
            fast = list;
            // 当再次相遇时循环退出
            while (fast != slow) {
                fast = fast->next;
                slow = slow->next;
            }
            return (fast);
        }
    }
    
    return (NULL);
}

static void
get_partial_length(node_t *list, node_t *node, int *length)
{
    // 链表非空
    *length = 1;
    if (list == node)
        return;
    
    while (list->next != node) {
        *length += 1;
        list = list->next;
    }
    
    // length包含了入环的第1个节点(即node)
    *length += 1;
}

static void
get_tail_and_length(node_t *list, node_t **tail, int *length)
{
    // 链表非空
    *length = 1;
    
    while (list->next) {
        *length += 1;
        list = list->next;
    }
    
    *tail = list;
}

node_t*
get_cross_node(node_t *list1, node_t *list2)
{
    // 空链表不相交
    if (!list1 || !list2) {
        return (NULL);
    }
    
    if (list1 == list2)
        return (list1);
    
    node_t *loop_node1 = get_loop_node(list1);
    node_t *loop_node2 = get_loop_node(list2);
    
    // 一个有环(入环节点非空), 一个无环(入环节点为空), 不相交
    if (!loop_node1 != !loop_node2)
        return (NULL);
    
    // 都有环而入环节点不同, 绕环一圈找交点, 找到则相交, 否则不相交
    if (loop_node1 && loop_node1 != loop_node2) {
        node_t *node = loop_node1->next;
        while (node != loop_node1) {
            if (node == loop_node2)
                return (node);
            node = node->next;
        }
        return (NULL);
    }
    
    int len1, len2;
    if (!loop_node1) { // 都无环的情况
        node_t *tail1, *tail2;
        
        get_tail_and_length(list1, &tail1, &len1);
        get_tail_and_length(list2, &tail2, &len2);
        
        // 尾部节点不同, 不相交
        if (tail1 != tail2)
            return (NULL);
    } else { // 入环节点相同的情况
        get_partial_length(list1, loop_node1, &len1);
        get_partial_length(list2, loop_node1, &len2);
    }
    
    // 如果链表长度不同, 则假定list1更长, 并调整它的长度, 与list2对齐
    int diff_cnt = 0;
    if (len1 > len2)
        diff_cnt = len1 - len2;
    else if (len1 < len2) {
        diff_cnt = len2 - len1;
        // 交换链表后, list1更长
        node_t *tmp = list1;
        list1 = list2;
        list2 = tmp;
    }
    
    // list1链表更长, 需要调整它与list2对齐
    while (diff_cnt-- > 0)
        list1 = list1->next;
    
    while (list1 != list2) {
        list1 = list1->next;
        list2 = list2->next;
    }
    
    return (list1);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小瓶子36

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值