关于数据结构的知识经常是互联网公司面试的重点内容,而对于链表的考察更是重中之重。经常会问到单链表中关于是否相交是否存在环的问题,笔者不才,自己想出一种方法。看到其他博客所写的规律,很难理解,并且缺乏证明。让我们一起来看看这个高频率的考点。1、判断链表是否相交,并且返回一个相交的节点。
链表是否相交,并不是你想的两条线相交的样子,一般考察的是链表只有一个next域的单链表。那就简单了许多。你可以想象两个人从两条路出发,最终走到一条路的情景。最形象的例子就是你衣服上的拉链了。因为只有一个next域,所以只要链表相交,从相交的那个节点开始,从该节点以后的所有的节点都是公共节点。请注意:data域相等并不是链表相交的判断条件,所以将链表的data和链表A交B所结合的思路都是错的。审题清楚了,怎样判断呢!
突破口就在这里:从相交的那个节点开始,从该节点以后的所有的节点都是公共节点。那我们是不是可以只看每个链表最后那个节点是不是公共节点呢!如果不是公共节点,那链表不想交,如果是,那就是链表相交。
怎样返回第一个节点呢!要想找到第一个节点,用蛮力法,那就是一个链表做模板,另一个链表每次都遍历一次,时间复杂度太高,不能让面试官满意。相交最好的情况就是链表是对称的。两个指针同时走,p = q 时候那就是第一个节点了。可惜事实并非完美。但是我们可以构造一个逻辑上接近完美的最好情况。如果我们想象把长的链表多余的部分去掉就好办了,那必须要知道链表的长度。接着上步,判断链表是否相交,找各自指向链表的两个指针一定得滑到最后一个节点,我们定义两个计数的变量来记录两个链表的长度。然后再次让指针从各自链表的开头开始走。只不过不是同时走。我们已经知道了链表的长度,找到比较长的链表,先用for循环让指向头结点的指针向前滑动m-n个节点。然后再同时滑动。就是构造了最好的情况。只需要两次遍历链表,不但可以知道是否相交,而且可以返回第一个相交的节点。如下图所示:
函数实现
Node *Fisrt_Node (List plist, List qlist) //返回相交的一个节点,如果返回为NULL,则没有交点
{
if(qlist == NULL || plist == NULL) //如果有一个链表为空,则两条链表不相交,
{
return NULL;
}
List p = plist->next;
List q = qlist->next;//定义两个指向链表的指针
int n = 0;
int m = 0;//定义两个计数器,分别计数两个链表的长度。
while (p->next != NULL)
{
n++;
p = p->next;
}
while (q->next != NULL)
{
m++;
q = q->next;
}
if ( p != q)//判断是否有公共节点,不能用数据域data判断。
{
return NULL;//没有相交,返回为NULL
}
if (m == n)//最好的情况,m = n;两个指向链表的指针p,q同时滑动,当P = q时;此节点为相交的第一个节点;返回即可
{
q = qlist;
p = plist;
while ( p != q)//p 和 q同时走
{
p = p->next;
q = q->next;
}
return p;
}
else if (m>n)//两条链表不一样长,
{
int i=0;
for (q = qlist; i<m-n; i++)//指向长的链表的指针先走m-n个节点,然后就构造了理想的最好情况,剩下的两条链表一样长了
{
q = q->next;
}
p = plist;
while ( p != q)
{
p = p->next;
q = q->next;
}
return p;
}
else
{
int i=0;
for (q = qlist; i<n-m; i++)
{
q = q->next;
}
q = qlist;
while ( p != q)
{
p = p->next;
q = q->next;
}
return p;
}
}
至于判断是否有交点的函数,请读者自己写,只要明白返回节点的函数,很简单就能判断是否相交。饮水思源。点个赞吧!