编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:在节点 c1 开始相交。
一:暴力法
遍历第一个链表( 长度为m ),每遍历到一个节点,就在第二个链表上( 长度为n ) 顺序遍历每个节点,如果在第二个链表上有一个节点和第一个链表上的节点一样,则说明2个链表在这个节点上重合,这个节点就是他们的公共节点。时间复杂度O(m*n)。
二:辅助栈法
直接上代码
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(!headA||!headB) return nullptr;
stack<ListNode*> sta1;
stack<ListNode*> sta2;
ListNode *p1=headA;
while(p1!=nullptr)
{
sta1.push(p1);
p1=p1->next;
}
ListNode *p2=headB;
while(p2!=nullptr)
{
sta2.push(p2);
p2=p2->next;
}
ListNode *curp=NULL;
//通过比较栈顶元素,如果相同,则弹出,直到找到最后一个相同的节点
while(!sta1.empty()&&!sta2.empty()&&sta1.top()==sta2.top())
{
curp=sta1.top();
sta1.pop();
sta2.pop();
}
return curp;
}
虽然时间复杂度O(m+n)得到了优化,但是增大了空间复杂度,不好。
三、不需要辅助栈
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(!headA||!headB) return nullptr;
//1、首先得到两个链表的长度
int longlist=getLength(headA);
int shortlist=getLength(headB);
//2、计算出2个链表的节点数之差
int lengthdif=longlist-shortlist;
ListNode *longhead=headA;
ListNode *shorthead=headB;
if(lengthdif<0)
{
longhead=headB;shorthead=headA;
lengthdif=shortlist-longlist;
}
//2、较长的链表先走若干步(这里的步数与节点数之差相同)
for (int i=0;i<lengthdif;i++)
{
longhead=longhead->next;
}
//3、同时遍历2个链表,直到找到他们第一个相同的节点
while(longhead!=nullptr&&shorthead!=nullptr&&longhead!=shorthead)
{
longhead=longhead->next;shorthead=shorthead->next;
}
return longhead;
}
//计算链表长度函数
int getLength(ListNode * head)
{
int count=0;
while(head!=nullptr)
{
head=head->next;
count++;
}
return count;
}
此方法提高了空间效率
四、快慢指针法
//通过将某一个链表的末尾和开头链接就可以将变成其判断是否有环以及环的入口问题。对于链表环路问题,可以使用快慢指针来解决
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(!headA||!headB) return nullptr;
//先构造环
ListNode *curp=headB;
while(curp->next)
{
curp=curp->next;
}
curp->next=headB;
//构建快慢指针
ListNode *fast=headA;
ListNode *slow=headA;
while(fast!=nullptr&&fast->next!=nullptr)
{
fast=fast->next->next;
slow=slow->next;
//第一次相遇
if(fast==slow)
{
fast=headA;
//第二次相遇
while(fast!=slow)
{
fast=fast->next;
slow=slow->next;
}
//将链表结构复原
curp->next=nullptr;
return fast;
}
}
curp->next=nullptr;
return nullptr;
}