判断两个链表是否相交并找出交点
判断两个链表是否相交并找出交点
问题描述:
一个比较经典的问题,判断两个链表是否相交,如果相交找出他们的交点。
第一种情况:两个链表均不含有环
思路:
1、直接法
采用暴力的方法,遍历两个链表,判断第一个链表的每个结点是否在第二个链表中,时间复杂度为O(len1*len2),耗时很大。
2、hash计数法
如 果 两个链表相交,则两个链表就会有共同的结点;而结点地址又是结点唯一标识。因而判断两个链表中是否存在地址一致的节点,就可以知道是否相交了。可以对第一 个链表的节点地址进行hash排序,建立hash表,然后针对第二个链表的每个节点的地址查询hash表,如果它在hash表中出现,则说明两个链表有共 同的结点。这个方法的时间复杂度为:O(max(len1+len2);但同时还得增加O(len1)的存储空间存储哈希表。这样减少了时间复杂度,增加 了存储空间。
以链表节点地址为值,遍历第一个链表,使用Hash保存所有节点地址值,结束条件为到最后一个节点(无环)或Hash中该地址值已经存在(有环)。
再遍历第二个链表,判断节点地址值是否已经存在于上面创建的Hash表中。
这个方面可以解决题目中的所有情况,时间复杂度为O(m+n),m和n分别是两个链表中节点数量。由于节点地址指针就是一个整型,假设链表都是在堆中动态创建的,可以使用堆的起始地址作为偏移量,以地址减去这个偏移量作为Hash函数
3、第三种思路是比较奇特的,在编程之美上看到的。先遍历第一个链表到他的尾部,然后将尾部的next指针指向第二个链表(尾部指针的next本来指向的是null)。这样两个链表就合成了一个链表,判断原来的两个链表是否相交也就转变成了判断新的链表是否有环的问题了:即判断单链表是否有环?
这样进行转换后就可以从链表头部进行判断了,其实并不用。通过简单的了解我们就很容易知道,如果新链表是有环的,那么原来第二个链表的头部一定在环上。因此我们就可以从第二个链表的头部进行遍历的,从而减少了时间复杂度(减少的时间复杂度是第一个链表的长度)。
下图是一个简单的演示:
这种方法可以判断两个链表是否相交,但不太容易找出他们的交点。
4、仔细研究两个链表,如果他们相交的话,那么他们最后的一个节点一定是相同的,否则是不相交的。因此判断两个链表是否相交就很简单了,分别遍历到两个链表的尾部,然后判断他们是否相同,如果相同,则相交;否则不相交。示意图如下:
判断出两个链表相交后就是判断他们的交点了。假设第一个链表长度为len1,第二个问len2,然后找出长度较长的,让长度较长的链表指针向后移动|len1 - len2| (len1-len2的绝对值),然后在开始遍历两个链表,判断节点是否相同即可。
下面给出一个简单的实现:
/************************************************************************
两个不含环的单链表的相交
相交指的是结点的地址相同,而不是结点的值相同
************************************************************************/
- typedef struct node_t
- {
- int data;//data
- struct node_t *next; //next
- }node;
- node* find_node(node *head1, node *head2)
- {
- if(NULL == head1 || NULL == head2)
- {
- return NULL;//如果有为空的链表,肯定是不相交的
- }
- node *p1, *p2;
- p1 = head1;
- p2 = head2;
- int len1 = 0;
- int len2 =0;
- int diff = 0;
- while(NULL != p1->next)
- {
- p1 = p1->next;
- len1++;
- }
- while(NULL != p2->next)
- {
- p2 = p2->next;
- len2++;
- }
- if(p1 != p2) //如果最后一个节点不相同,返回NULL
- {
- return NULL;
- }
- diff = abs(len1 - len2);
- if(len1 > len2)
- {
- p1 = head1;
- p2 = head2;
- }
- else
- {
- p1 = head2;
- p2 = head1;
- }
- for(int i=0; i<diff; i++)
- {
- p1 = p1->next;
- }
- while(p1 != p2)
- {
- p1 = p1->next;
- p2 = p2->next;
- }
- return p1;
- }
通过上面的操作就可以找到两个链表的交点了。
5、总结
上面的几种方法中最后一种是比较不错的,当然hash也是可以的。
问题的延伸:
如果原来的两个链表中有环怎么处理?
二、链表中有环时
如果链表有环且相交,那么这两个链表都是有环的。
找到第一个链表的环点,然后将环断开(当然不要忘记了保存它的下一个节点),然后再来遍历第二个链表,如果发现第二个链表从有环变成了无环,那么他们就是相交的嘛,否则就是不相交的了。
当两个有环的链表相交时,有以下两种情况:
在这种情况下,两个链表的交点在环点之前,可以将环点切断,这样就变成了两个无环的链表求相交点。可使用以上方法。
另一种情况为:
在这种情况下,不存在所谓的相交点。
问题描述:
一个比较经典的问题,判断两个链表是否相交,如果相交找出他们的交点。
第一种情况:两个链表均不含有环
思路:
1、直接法
采用暴力的方法,遍历两个链表,判断第一个链表的每个结点是否在第二个链表中,时间复杂度为O(len1*len2),耗时很大。
2、hash计数法
如 果 两个链表相交,则两个链表就会有共同的结点;而结点地址又是结点唯一标识。因而判断两个链表中是否存在地址一致的节点,就可以知道是否相交了。可以对第一 个链表的节点地址进行hash排序,建立hash表,然后针对第二个链表的每个节点的地址查询hash表,如果它在hash表中出现,则说明两个链表有共 同的结点。这个方法的时间复杂度为:O(max(len1+len2);但同时还得增加O(len1)的存储空间存储哈希表。这样减少了时间复杂度,增加 了存储空间。
以链表节点地址为值,遍历第一个链表,使用Hash保存所有节点地址值,结束条件为到最后一个节点(无环)或Hash中该地址值已经存在(有环)。
再遍历第二个链表,判断节点地址值是否已经存在于上面创建的Hash表中。
这个方面可以解决题目中的所有情况,时间复杂度为O(m+n),m和n分别是两个链表中节点数量。由于节点地址指针就是一个整型,假设链表都是在堆中动态创建的,可以使用堆的起始地址作为偏移量,以地址减去这个偏移量作为Hash函数
3、第三种思路是比较奇特的,在编程之美上看到的。先遍历第一个链表到他的尾部,然后将尾部的next指针指向第二个链表(尾部指针的next本来指向的是null)。这样两个链表就合成了一个链表,判断原来的两个链表是否相交也就转变成了判断新的链表是否有环的问题了:即判断单链表是否有环?
这样进行转换后就可以从链表头部进行判断了,其实并不用。通过简单的了解我们就很容易知道,如果新链表是有环的,那么原来第二个链表的头部一定在环上。因此我们就可以从第二个链表的头部进行遍历的,从而减少了时间复杂度(减少的时间复杂度是第一个链表的长度)。
下图是一个简单的演示:
这种方法可以判断两个链表是否相交,但不太容易找出他们的交点。
4、仔细研究两个链表,如果他们相交的话,那么他们最后的一个节点一定是相同的,否则是不相交的。因此判断两个链表是否相交就很简单了,分别遍历到两个链表的尾部,然后判断他们是否相同,如果相同,则相交;否则不相交。示意图如下:
判断出两个链表相交后就是判断他们的交点了。假设第一个链表长度为len1,第二个问len2,然后找出长度较长的,让长度较长的链表指针向后移动|len1 - len2| (len1-len2的绝对值),然后在开始遍历两个链表,判断节点是否相同即可。
下面给出一个简单的实现:
/************************************************************************
两个不含环的单链表的相交
相交指的是结点的地址相同,而不是结点的值相同
************************************************************************/
- typedef struct node_t
- {
- int data;//data
- struct node_t *next; //next
- }node;
- node* find_node(node *head1, node *head2)
- {
- if(NULL == head1 || NULL == head2)
- {
- return NULL;//如果有为空的链表,肯定是不相交的
- }
- node *p1, *p2;
- p1 = head1;
- p2 = head2;
- int len1 = 0;
- int len2 =0;
- int diff = 0;
- while(NULL != p1->next)
- {
- p1 = p1->next;
- len1++;
- }
- while(NULL != p2->next)
- {
- p2 = p2->next;
- len2++;
- }
- if(p1 != p2) //如果最后一个节点不相同,返回NULL
- {
- return NULL;
- }
- diff = abs(len1 - len2);
- if(len1 > len2)
- {
- p1 = head1;
- p2 = head2;
- }
- else
- {
- p1 = head2;
- p2 = head1;
- }
- for(int i=0; i<diff; i++)
- {
- p1 = p1->next;
- }
- while(p1 != p2)
- {
- p1 = p1->next;
- p2 = p2->next;
- }
- return p1;
- }
通过上面的操作就可以找到两个链表的交点了。
5、总结
上面的几种方法中最后一种是比较不错的,当然hash也是可以的。
问题的延伸:
如果原来的两个链表中有环怎么处理?
二、链表中有环时
如果链表有环且相交,那么这两个链表都是有环的。
找到第一个链表的环点,然后将环断开(当然不要忘记了保存它的下一个节点),然后再来遍历第二个链表,如果发现第二个链表从有环变成了无环,那么他们就是相交的嘛,否则就是不相交的了。
当两个有环的链表相交时,有以下两种情况:
在这种情况下,两个链表的交点在环点之前,可以将环点切断,这样就变成了两个无环的链表求相交点。可使用以上方法。
另一种情况为:
在这种情况下,不存在所谓的相交点。