解法:
通过遍历两链表到结束处的尾指针,并保存两链表的长度,判断尾指针是否相同,相同则是相交的链表,否则不相交。
在相交的基础上,根据链表的长度差,让较长的链表从头指针开始先移动差值的次数,然后两者指针一起移动,直到结点指针值相同,该值即为相交开始的结点。时间复杂度O(n),空间复杂度常数级。
自己做题时没想到该方法,主要是两个思路:
1.在遍历第一个链表时使用一个hashmap将结点指针存入,在遍历第二个链表时对每个结点判断是否在图中存在该结点,存在时该结点即为相交开始的结点,否则一直到结尾均无法查找到,查找过程使用hashmap,占用空间O(n),查找O(n),由于遍历链表也是O(n),最终空间复杂度O(n),时间复杂度O(n)。
2.先遍历第一个链表,记录尾指针和长度L1,然后遍历第二个链表,记录尾指针和长度L2,根据尾指针判断是否相交。在相交时,将链表长度较短的进行反转,然后再遍历较长的链表获取长度Lr。为表述方便,这里假定L1>=L2,则:
L1为链表1不重合部分,两链表重合起始处,链表重合后续部分;
Lr为链表1不重合部分,两链表重合起始处,链表2不重合部分;
显然,将L2后续重合部分设为La,非重合部分设为Lb,可以认为L1-Lr=La-Lb,结合La+Lb+1=L2,即可获得后续重合区长度La=(L1+L2-Lr-1)/2,根据该长度从反转完毕的L2头指针处移动Lk次,此时恰好为开始重合的结点。整体也可以认为是O(n)的时间复杂的和常数级的空间复杂度,但相当憨憨。
思路2存档记载自己的憨憨程度,根据思路大致实现,没有进行测试。
struct Linklist {
int val;
Linklist* next;
};
Linklist* getNode(Linklist* L1, Linklist* L2, int Len1, int Len2) {
//调用该函数时确定有重合,不做特殊处理
Linklist* L2_ptr = L2, * L_re = nullptr, * L_next = nullptr;
if (Len2 == 1)
return L2;//短链长度为1且相交,直接返回1
while (L2_ptr != nullptr) {//反转链表L2,最后L2_ptr指向尾指针后,L_re指向尾指针
L_next = L2_ptr->next;
L2_ptr->next = L_re;
L_re = L2_ptr;
L2_ptr = L_next;
}
Linklist* L1_ptr = L1;
int Len_r = 0;
while (L1_ptr != nullptr) {//重合部分已经反转,再次遍历L1获取长度
L1_ptr = L1_ptr->next;
++Len_r;
}
Len_r = (Len1 + Len2 - Len_r - 1) > 1;
while (Len_r--)
L_re = L_re->next;//L_re从L2尾指针开始,可以看成从尾指针后退Len_r步
return L_re;
}
Linklist* judge(Linklist* L1, Linklist* L2) {
if (L1 == nullptr || L2 == nullptr)
return nullptr;//其中有空
int Len1 = 0, Len2 = 0;
Linklist* L1_ptr = L1, * L2_ptr = L2;
while (L1_ptr != nullptr){//遍历链表L1,L1_ptr为尾指针
L1_ptr = L1_ptr->next;
++Len1;
}
while (L2_ptr != nullptr){//遍历链表L2,L2_ptr为尾指针
L2_ptr = L2_ptr->next;
++Len2;
}
if (L1_ptr != L2_ptr)
return nullptr;//不重合
//到达此处已经重合
if (Len1 > Len2)
return getNode(L1, L2, Len1, Len2);
return getNode(L2, L1, Len2, Len1);//保证较长的链表放前面
}