问题描述:
如果两个链表相交,返回交点;否则,返回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);
}