C++程序 查找两个链表交点
系统中有两个单链表。由于某个程序错误,其中一个链表的尾节点与第二个链表连接在一起,形成一个倒Y形的链表。编写程序获取两个链表合并的点。
上图显示一个具有交点为15的两个链表的示例。
方法一(简单地使用两个循环):
使用2个嵌套的for循环。外部循环将为第一个列表的每个节点,内部循环将为第二个列表。在内部循环中,检查第二个列表中的任何节点是否与第一个链表的当前节点相同。这种方法的时间复杂度将为O(M * N),其中m和n是两个列表中的节点数。
方法二(标记已访问的节点):
此解决方案需要修改基本的链表数据结构。每个节点都有一个visited标志。遍历第一个链表并保持标记已访问的节点。现在遍历第二个链表,如果再次看到已访问的节点,则存在交点,请返回交点节点。此解决方案在O(m + n)中工作,但需要每个节点的额外信息。可以使用哈希实现不需要修改基本数据结构的此解决方案变体。遍历第一个链表并在哈希中存储已访问节点的地址。现在遍历第二个链表,如果看到哈希中已经存在的地址,则返回交点节点。
方法三(使用节点计数的差异):
- 获取第一个列表中节点的数量,将其计数为c1。
- 获取第二个列表中节点的数量,将其计数为c2。
- 获取计数之差 d = abs(c1 – c2)
- 现在从第一个节点开始遍历更大的列表,直到d个节点,以便从这里开始两个列表具有相等的节点数
- 然后我们可以同时遍历两个列表,直到遇到公共节点。 (请注意,获取公共节点是通过比较节点的地址完成的)
下图是上述方法的运行过程:
下面是上述方法的实现:
输出:
时间复杂度: O(m+n)
辅助空间: O(1)
方法4(在第一个列表中创建圆圈):
感谢 Saravanan Man 提供以下解决方案。
1. 遍历第一个链接列表(计算元素的数量),并创建一个循环链接列表。(记住最后一个节点,以便以后可以打破循环)。
2. 现在将问题视为查找第二个链接列表中的循环。 因此,问题得到解决。
3. 由于我们已经知道循环的长度(第一个链接列表的大小),所以我们可以在第二个列表中遍历这些数字,然后从第二个列表的开头开始另一个指针。 我们必须遍历直到它们相等,这就是所需的交点。
4. 从链表中删除圆圈。
时间复杂度: O(m+n)
辅助空间: O(1)
方法5(反转第一个列表并创建方程):
1) 让X为第一个交点到交点的链表长度。
让Y为第二个交点到交点的链表长度。
让Z为包括交点节点在内的链接列表的从交点到末尾的长度。
我们有
X + Z = C1;
Y + Z = C2;
2) 反转第一个链接列表。
3) 遍历第二个链接列表。 让C3为第二个列表的长度-1。
现在我们有
X + Y = C3
我们有3个线性方程。 通过解决它们,我们得到
X = (C1 + C3 – C2)/2;
Y = (C2 + C3 – C1)/2;
Z = (C1 + C2 – C3)/2;
我们找到了交点。
4) 反转回第一个链接列表。
优点: 不需要比较指针。
缺点: 修改链接列表(反转列表)。
时间复杂度: O(m+n)
辅助空间: O(1)
方法6(遍历两个列表并比较最后节点地址): 此方法仅用于检测是否存在交点。(感谢NeoTheSaviour提出这个想法)
1) 遍历第1个列表,存储最后节点地址
2) 遍历第2个列表,存储最后节点地址。
3) 如果存储在1和2中的节点相同,则它们相交。
此方法的时间复杂度为O(m+n),使用的辅助空间为O(1)
方法7(双指针技术):
使用两个指针:
- 将ptr1和ptr2初始化为head1和head2。
- 逐个节点遍历列表。
- 当ptr1到达列表的末端时,将其重定向到head2。
- 类似地,当ptr2到达列表的末端时,将其重定向到head1。
- 一旦它们都通过重新分配,它们将与碰撞点等距离。
- 如果在任何节点上ptr1遇到ptr2,则它是交点。
- 第二次迭代如果没有交点则返回NULL。
输出:
时间复杂度: O( m + n)
辅助空间: O(1)