两个链表相交,计算相交点

本文介绍了一种高效的方法来查找两个链表的相交节点。通过计算链表长度并使指针同步,实现相交节点的定位。此外,文章还展示了如何将相交链表转换为环状结构,并通过快慢指针找到环的起始点。

来源http://blog.youkuaiyun.com/huangxy10/article/details/8014276

思路在8中有介绍

分别计算两链表的长度,为M,N。让指向长链表的指针先走,让两链表等长,

再两指针一起走,第一次相遇点即为相交点。

如果把相交链表变成一个环,则环的第一个结点即为相交点。


  1. // LinkTable.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. #include <string>  
  7. using namespace std;  
  8.   
  9. //链表的结构体  
  10. struct node  
  11. {  
  12.     char val;  
  13.     node * next;  
  14. };  
  15.   
  16. //建立链表  
  17. struct node * create( string & str_link )  
  18. {  
  19.     int len = str_link.length();  
  20.   
  21.     struct node * phead = new node();     //带有表头的链表,表头中不存储任何元素  
  22.     struct node * preNode = phead;  
  23.     forint i=0; i<len; i++ )  
  24.     {  
  25.         struct node * pNode = new node();  
  26.         pNode->val = str_link[i];  
  27.         pNode->next = NULL;  
  28.         preNode->next = pNode;  
  29.         preNode = pNode;  
  30.     }  
  31.     return phead;  
  32. }  
  33.   
  34. //输出链表  
  35. void out_link( struct node * phead )  
  36. {  
  37.     if( phead == NULL )  
  38.         return;  
  39.     struct node * pNode = phead->next;  
  40.     while( pNode )  
  41.     {  
  42.         cout <<pNode->val;  
  43.         pNode = pNode->next;  
  44.     }  
  45.     cout << endl;  
  46. }  
  47.   
  48. //找到第index个元素  
  49. struct node * find_node(struct node* phead, int index )  
  50. {  
  51.     if(!phead) return NULL;  
  52.     struct node * pNode = phead;  
  53.     while( index--)  
  54.     {  
  55.         pNode = pNode->next;  
  56.         if( !pNode )  
  57.             return NULL;  
  58.     }  
  59.     return pNode;  
  60. }  
  61.   
  62. //检查链表有无环  
  63. //有,则返回快慢指针共同指向的点  
  64. //无,则返回空指针  
  65. struct node * check_loop( struct node * phead )  
  66. {  
  67.     if(!phead)  
  68.          return NULL;  
  69.     struct node * pFast = phead;  
  70.     struct node * pSlow = phead;  
  71.   
  72.     int step = 1;  
  73.     while( pFast )  
  74.     {  
  75.         pFast = pFast->next;  
  76.         if( step++%2==0 )  
  77.         {  
  78.             pSlow = pSlow->next;  
  79.             if( pSlow == pFast )  
  80.                 return pSlow;  
  81.         }  
  82.     }  
  83.     return NULL;  
  84. }  
  85.   
  86. //求两个节点的距离,即中间有多少个连线  
  87. //返回-1为出错,如果是同一节点,则返回0  
  88. int find_length( struct node* pNode1, struct node* pNode2 )  
  89. {  
  90.     if(!pNode1||!pNode2) return -1;  
  91.   
  92.     int len=0;  
  93.     while( pNode1 )  
  94.     {  
  95.         if( pNode1 == pNode2 )  
  96.             return len;  
  97.         pNode1 = pNode1->next;  
  98.         len++;  
  99.     }  
  100.     return -1;  
  101. }  
  102.   
  103. //找环的起始点,pTail为快慢指针共同指向的节点  
  104. struct node * loop_first_node( struct node * phead, struct node * pTail )  
  105. {  
  106.     if( !phead || !pTail ) return NULL;  
  107.     struct node * pNode1 = pTail->next;  
  108.     struct node * pNode2 = phead->next;  
  109.     int M = find_length( pNode1, pTail );  
  110.     int N = find_length( pNode2, pTail );  
  111.   
  112.     if( M > N )  
  113.     {  
  114.         int step = M-N;  
  115.         while(step--)  
  116.             pNode1 = pNode1->next;  
  117.     }  
  118.     if( N > M )  
  119.     {  
  120.         int step = N-M;  
  121.         while(step--)  
  122.             pNode2 = pNode2->next;  
  123.     }  
  124.     while(pNode1&&pNode2)  
  125.     {  
  126.         if(pNode1 == pNode2 )  
  127.             return pNode1;  
  128.         pNode1 = pNode1->next;  
  129.         pNode2 = pNode2->next;  
  130.     }  
  131.     return NULL;  
  132. }  
  133.   
  134. void test()  
  135. {  
  136.     string str;  
  137.     cout << "Input the first link:"<<endl;  
  138.     cin >> str;  
  139.     struct node *phead1 = create( str );  
  140.       
  141.     int index;  
  142.     cout << "Input the index of cross node: " <<endl;  
  143.     cin >> index;  
  144.     struct node * pNode1 = find_node( phead1, index );  
  145.     cout << "Input the second link:"<<endl;  
  146.     cin >> str;  
  147.     struct node *phead2 = create( str );  
  148.   
  149.     struct node * pNode = phead2;  
  150.     while( pNode->next )  
  151.         pNode = pNode->next;  
  152.     pNode->next = pNode1;        //生成相交链表, 注释掉这一行则为不相交  
  153.   
  154.     while( pNode->next )           //找到链表1的尾结点  
  155.         pNode = pNode->next;  
  156.     pNode->next = phead2->next;    //连接链表2的首结点  
  157.     struct node * pTail = check_loop( phead1 ); //检查是否有环  
  158.     struct node * pFirstLoopNode = NULL;  
  159.     if( pTail )  
  160.     {     
  161.         cout <<"cross." <<endl;  
  162.         pFirstLoopNode = loop_first_node(phead1, pTail);  
  163.         cout <<"The cross node is: "<< pFirstLoopNode->val << endl;  
  164.         pNode->next = NULL;            //还原,把环拆开  
  165.     }  
  166.     else  
  167.         cout << "no cross." <<endl;  
  168.   
  169. }  
  170.   
  171. int _tmain(int argc, _TCHAR* argv[])  
  172. {  
  173.     test();  
  174.     return 0;  
  175. }  

<think>首先,用户的问题是:“检测两个链表是否相交的算法”。用户提供了几个引用,我需要基于这些构建回答。用户说“参考站内引用”,所以我应该引用这些来源。 用户提供的引用: - [^1]: 解释链表相交的定义。 - [^2]: 左神算法,涉及链表是否有环、两无环链表是否相交等。提到了计算长度差的方法。 - [^3]: 通过比较尾结来判断是否相交。 - [^4]: 类似[^2],提到了计算长度差的方法。 用户强调:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 但在这个对话中,这是第一次用户的问题,所以我应该专注于当前问题。系统指令说“回答中引用的段落末尾自然地添加引用标识”,所以我必须在回答中引用这些来源。 系统级指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答结构清晰,逐步解决问题。 用户的问题是关于检测两个链表是否相交的算法。我需要提供一个清晰的、逐步的解释。 关键从引用中: - 判断两个单链表是否相交:检查是否有公共节。 - 方法1(从[^3]):比较尾结。如果尾结相同,则相交;否则,不相交。 - 方法2(从[^2]和[^4]):计算链表长度差,移动长链表的指针到相同起始,然后同步遍历比较节。 此外,[^2]提到了链表是否有环的问题,但用户的问题似乎专注于无环链表,因为引用[^1]说“单链表”,假设无环。我应该确认是否是单链表无环的情况。 用户说“检测两个链表是否相交”,没有指定是否有环。引用[^2]提到了有环和无环的情况。但为了简单,我应该先从无环链表开始。 步骤: 1. 确认链表类型:假设是单链表,可能无环或有环。 2. 对于无环链表: - 方法A(尾结比较):如果两个链表的尾结相同,则相交;否则,不相交。 - 方法B(长度差):计算两个链表的长度差,移动长链表的头指针到相同位置,然后同步遍历直到找到相同节或结束。 3. 如果有环,情况更复杂,需要先判断是否有环。 但用户的问题可能针对简单情况。我应该从基础开始。 回答结构: - 简介:解释什么是链表相交。 - 算法描述:逐步解释检测方法。 - 代码示例:提供伪代码或编程语言代码。 - 相关问题:生成相关问题。 使用中文回答。 生成相关问题在最后。 现在,算法描述: 1. **尾结比较法**(简单有效): - 遍历链表1到尾部,保存尾结。 - 遍历链表2到尾部,保存尾结。 - 比较两个尾结:如果相同,则链表相交;否则,不相交。 - 时间复杂度:O(n+m),其中n和m是链表长度。 - 空间复杂度:O(1),只需存储尾结。 2. **长度差法**(可以找到相交): - 计算链表1的长度len1和尾结。 - 计算链表2的长度len2和尾结。 - 如果尾结不同,则不相交;否则,相交。 - 然后,为了找到相交计算长度差diff = |len1 - len2|。 - 让长链表的指针先移动diff步。 - 然后两个指针同步移动,直到找到相同节。 - 时间复杂度:O(n+m)。 - 空间复杂度:O(1)。 尾结比较法可以判断是否相交,但不能直接找到相交。长度差法可以判断并找到相交。 用户的问题是“检测是否相交”,所以尾结比较法可能足够。 从效率上,两者都高效。 现在,代码示例:我应该提供伪代码或具体语言代码。用户引用中提到C++和Java,所以我可以选择一个通用语言或伪代码。 生成相关问题:如“如何找到相交?”,“如果链表有环怎么办?”等。 添加引用:在相关段落末尾添加引用标识。 数学表达式:链表问题通常不需要数学,但如果有公式,比如长度差,可以使用行内格式。 例如:计算长度差diff = |len1 - len2|,写成行内数学:$diff = |len1 - len2|$。 独立公式可能不需要。 回答结构: - 问题背景 - 方法1:尾结比较(引用[^3]) - 方法2:长度差法(引用[^2]和[^4]) - 其他情况(如有环) - 总结 - 代码示例 - 相关问题 其他情况:链表可能有环,引用[^2]讨论了有环链表相交的问题。我应该简要提及。 但保持焦在用户需求上:检测是否相交。 </think>### 检测两个链表是否相交的算法 在计算机科学中,判断两个链表是否相交是一个常见的问题,特别是在链表操作中(如链表合并、循环检测等)。链表相交指的是两个单链表是否存在至少一个公共节。如果存在公共节,那么从这个节开始,后续所有节都是相同的,因为单链表的每个节只有一个指向下一个节的指针[^1]。下面我将逐步解释检测算法,确保结构清晰、可靠。算法主要针对无环单链表(最常见情况),最后会简要讨论有环链表的情况。时间复杂度通常为$O(n + m)$(其中$n$和$m$是链表长度),空间复杂度为$O(1)$。 #### 核心算法:无环链表检测 对于无环单链表,有两种高效算法可以检测是否相交。两种方法都基于一个关键原理:如果链表相交,它们必有相同的尾结(即链表末尾节)。因此,判断尾结是否相等是基础步骤。以下是详细步骤: 1. **尾结比较法(简单高效,仅判断是否相交)** 这种方法通过比较两个链表的尾结来直接判断是否相交。如果尾结相同,则链表相交;否则,不相交。算法步骤如下: - 遍历链表1,找到尾结(即最后一个节)。 - 遍历链表2,找到尾结。 - 比较两个尾结: - 如果尾结相同(内存地址相等),则链表相交(因为相交链表共享相同的尾部)。 - 如果尾结不同,则链表相交。 - 时间复杂度:$O(n + m)$(每个链表遍历一次)。 - 空间复杂度:$O(1)$(仅需存储尾结指针)。 - 优:实现简单,代码易读。 - 缺:只能判断是否相交,无法直接找到相交(首个公共节)。 - 伪代码示例(基于引用[^3]): ```java // 辅助函数:找到链表的尾结 Node findEnd(Node head) { if (head == null) return null; Node current = head; while (current.next != null) { current = current.next; } return current; } // 检测是否相交 boolean isIntersect(Node head1, Node head2) { Node end1 = findEnd(head1); // 找到链表1的尾结 Node end2 = findEnd(head2); // 找到链表2的尾结 return end1 == end2; // 比较尾结是否相同 } ``` 2. **长度差法(可同时判断相交和找到相交)** 这种方法在尾结比较基础上,额外计算链表长度差,从而不仅能判断是否相交,还能找到相交。算法步骤如下: - 遍历链表1,计算长度$len1$并保存尾结。 - 遍历链表2,计算长度$len2$并保存尾结。 - 比较尾结: - 如果尾结不同,则链表相交(直接返回失败)。 - 如果尾结相同,则链表相交(继续下一步)。 - 计算长度差:$diff = |len1 - len2|$。 - 移动长链表的头指针$diff$步(例如,如果$len1 > len2$,将链表1的指针移动$diff$步)。 - 同步遍历两个链表:每次移动两个链表的指针各一步。 - 当指针指向同一个节时,该节就是首个相交。 - 如果遍历到末尾(null)仍未找到相同节,则无相交(理论上不会发生,因为尾结相同已确保相交)。 - 时间复杂度:$O(n + m)$(遍历长度和同步遍历各一次)。 - 空间复杂度:$O(1)$(只需存储长度和指针)。 - 优:可找到相交,适用于需要定位的场景。 - 缺:代码稍复杂。 - 伪代码示例(基于引用[^2]和[^4]): ```java // 辅助函数:计算链表长度和尾结 int[] getLengthAndEnd(Node head) { if (head == null) return new int[]{0, null}; int len = 1; Node current = head; while (current.next != null) { current = current.next; len++; } return new int[]{len, current}; // 返回长度和尾结 } // 检测是否相交(返回相交或null) Node findIntersection(Node head1, Node head2) { // 获取链表1的长度和尾结 int[] info1 = getLengthAndEnd(head1); int len1 = info1[0]; Node end1 = info1[1]; // 获取链表2的长度和尾结 int[] info2 = getLengthAndEnd(head2); int len2 = info2[0]; Node end2 = info2[1]; // 比较尾结:如果不同,则不相交 if (end1 != end2) { return null; } // 计算长度差,移动长链表的指针 Node p1 = head1; Node p2 = head2; int diff = Math.abs(len1 - len2); if (len1 > len2) { for (int i = 0; i < diff; i++) { p1 = p1.next; } } else if (len2 > len1) { for (int i = 0; i < diff; i++) { p2 = p2.next; } } // 同步遍历直到找到相交 while (p1 != p2) { p1 = p1.next; p2 = p2.next; } return p1; // p1 和 p2 相等时即为相交 } ``` #### 其他情况:有环链表的处理 如果链表可能含有环(即节指向自身或先前节),
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值