目录
题目
相交链表
题目要求
示例
链接:题目链接
解答
方法一、
自己写的暴力求解法
实现思路
先算出listA的长度,然后算出listB的长度,判断两个链表哪一个比较长,然后让长的链表先走abs(lenA-lenB),然后再将两个指针一起向后访问链表,并且判断两个指针指向的结点是否相等,如果两个指针指向的结点相同时,此结点就为两个链表的相交结点。
时间复杂度和空间复杂度
时间复杂度:O(N)
空间复杂度:O(1)
代码
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
if(headA==NULL||headB==NULL)
{
return NULL;
}
int lenA = 0;
int lenB = 0;
struct ListNode * currA = headA;
struct ListNode * currB = headB;
while(currA!=NULL)
{
currA=currA->next;
lenA++;
}
while(currB!=NULL)
{
currB=currB->next;
lenB++;
}
currA=headA;
currB=headB;
if(lenA>lenB)
{
int k = lenA-lenB;
while(k)
{
currA=currA->next;
k--;
}
while(currA!=NULL&&currB!=NULL)
{
if((currA)==(currB))
{
return currA;
}
currA=currA->next;
currB=currB->next;
}
return NULL;
}
else
{
int k = lenB-lenA;
while(k)
{
currB=currB->next;
k--;
}
while(currA!=NULL&&currB!=NULL)
{
if((currA)==(currB))
{
return currA;
}
currA=currA->next;
currB=currB->next;
}
return NULL;
}
}
方法二、
第一种方法的改善
实现思路
实现思路和第一种方法相似,就是判断时少了一些判断。
时间复杂度和空间复杂度
时间复杂度:O(N)
空间复杂度:O(1)
代码
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
if(headA==NULL||headB==NULL)
{
return NULL;
}
int lenA = 1;
int lenB = 1;
struct ListNode * currA = headA;
struct ListNode * currB = headB;
while(currA->next!=NULL)
{
currA=currA->next;
lenA++;
}
while(currB->next!=NULL)
{
currB=currB->next;
lenB++;
}
if(currA!=currB)
{
return NULL;
}
struct ListNode * shortList = headA;
struct ListNode * longList = headB;
if(lenA>lenB)
{
shortList=headB;
longList=headA;
}
int gap = abs(lenA-lenB);
while(gap--)
{
longList=longList->next;
}
while(shortList!=longList)
{
shortList=shortList->next;
longList=longList->next;
}
return shortList;
}
方法三、
双指针法
实现思路
将两个指针currA和currB指向两个链表的头结点,当currA和currB指向NULL时,就让currA从headB开始重新遍历listB,让currB从headA开始重新遍历listA。当两个指针指向同一个结点时,说明该结点为两个链表的相交结点,当最后两个指针指向的结点都没有相等时,此时两个指针都指向NULL,就说明两个链表没有相交结点,返回NULL。
时间复杂度和空间复杂度
时间复杂度:O(A+B)
空间复杂度:O(1)
代码
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
if(headA==NULL||headB==NULL)
{
return NULL;
}
struct ListNode * currA = headA;
struct ListNode * currB = headB;
while(currA!=currB)
//currA和currB将两个表都遍历一遍,此时currA和currB遍历的结点数一样,为A+B的结点数,
//所以如果A和B有相交结点时,此时currA==currB,会跳出循环,此时currA和currB指向的都是相交的首结点
//如果遍历到最后还没有跳出循环,则就currA和currB都指向NULL时跳出循环,此时没有相交结点,返回NULL。
{
currA = currA==NULL ? headB : currA->next;
currB = currB==NULL ? headA : currB->next;
}
return currA;
}
方法四、
变为环求环的入口点
实现思路
将两个链表的尾结点的next指向一个链表的头结点,此时两个链表变为一个带环的链表,求出带环链表的入环结点就是两个链表的相交结点。此方法调用了求环形链表入环点的方法,但是力扣的习题判定了该方法改变了链表结构,所以测试不会给予通过。
显示的结果都是对的,但是因为改变了链表结构,所以测试不会通过
时间复杂度和空间复杂度
代码
struct ListNode *detectCycle(struct ListNode *head) {
if(head==NULL||head->next==NULL)
{
return NULL;
}
struct ListNode * slow = head;
struct ListNode * fast = head;
while((fast!=NULL)&&(fast->next!=NULL))
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
struct ListNode * start = head;
while(start!=slow)
{
start=start->next;
slow=slow->next;
}
return slow;
}
}
return NULL;
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
if(headA==NULL||headB==NULL)
{
return NULL;
}
struct ListNode * tail = headB;
while((tail->next)!=NULL)
{
tail=tail->next;
}
tail->next = headB;
return detectCycle(headA);
}
总结
该题的第一个和第二个方法类似,都是先求出两个链表的长度,然后让长的链表先走,然后再让两个链表一起向后走。
第三个方法使用两个指针,这两个指针都将两个链表遍历了一遍,当在相交点时,两个指针遍历的结点数是一样的,所以两个指针相遇的第一个结点就是两个链表的相交点。
第四个方法就是环形链表的方法的逆置,只是提供一个思路,但是实现起来的时间复杂度会比正常方法的时间复杂度大很多。