题目
编写一个程序,找到两个单链表相交的起始节点。
思路设计
首先明确一点: 判断两个链表是否相交的条件是 判断相关两个节点的地址是否相同,不是节点存储的值是否相同
方法一 暴力遍历
用一个链表的每一个节点去匹配另一个链表的节点,当两个节点的地址相同时,此节点就是相交节点。大家有兴趣的可以试试。
方法二
因为两个链表的公共部分长度一样,但非公共部分一般是不一样的,如果我们能屏蔽掉非公共部分的长度影响,那么就可以直接使用一个for循环同时对两个链表进行遍历来寻找相交节点了。
屏蔽长度差影响方法一
- 先求出两个链表的长度,再求出长度差值dis。
- 让一个指针P1先从较长链表的头节点出发,走长度差值dis个节点,这样就屏蔽掉了链表之间的长度插;然后与从短链表头节点的另一个指针P2同时开始遍历各自对应链表,每到一个节点就互相判断地址是否相同。
上代码
/**
* 求链表长度
*/
int getLength(struct ListNode *list)
{
int length = 0;
while (list)
{
length++;
list = list->next;
}
return length;
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
if (!headA || !headB) //任一链表为空则不存在相交可能
{
return NULL;
}
struct ListNode *nodeA = headA;
struct ListNode *nodeB = headB;
int lengthOfListA = getLength(nodeA);
int lengthOfListB = getLength(nodeB);
int dis = (lengthOfListA < lengthOfListB) ? (lengthOfListB-lengthOfListA) : (lengthOfListA - lengthOfListB); //链表长度差
if (lengthOfListA > lengthOfListB) //找到较长链表,让其指针先走差值dis个节点
{
for (int i = 0; i < dis; i++)
{
nodeA = nodeA->next;
}
}
else
{
for (int i = 0; i < dis; i++)
{
nodeB = nodeB->next;
}
}
while (nodeA != nodeB) // 屏蔽掉长度差后,同步遍历两个链表,找到相交节点后跳出循环;若没有相交节点,nodeA取值为null
{
nodeA = nodeA->next;
nodeB = nodeB->next;
}
return nodeA;
}
复杂度分析
时间复杂度
for循环执行了dis次,然后while循环K次,总的执行次数肯定小于等于长链表长度,所以时间复杂度就取O(n)
空间复杂度
我们只使用了两个指针,所以空间复杂度为O(1)
屏蔽长度差影响方法二
- 让短链表指针P1、长链表指针P2同时从各自表头遍历
- 当短链表指针遍历完短链表后接着遍历长链表,长链表指针遍历完自己后接着遍历短链表,当两个指针所指节点地址一样时,这个节点就是相交节点。
为方便解释我就引用大佬的图(原图地址就在leetCode 160题题解评论区):
上代码
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
if (!headA || !headB)
{
return NULL;
}
struct ListNode *nodeA = headA;
struct ListNode *nodeB = headB;
while (nodeA != nodeB) //没有找到相交节点前
{
nodeA = nodeA == NULL ? headB:nodeA->next; //A链表没有遍历完就一直遍历,如果遍历完就接着从B链表遍历
nodeB = nodeB == NULL ? headB:nodeB->next; // B同上,当两个节点地址相同时结束循环
}
return nodeA;
}
复杂度分析
时间复杂度
只使用了while循环,执行次数是链表长度的常数倍,所以时间复杂度还是O(n)
空间复杂度
我们只使用了两个指针,所以空间复杂度为O(1)