题目描述
输入两个链表,找出它们的第一个公共节点,链表结点定义如下:
public class ListNode
{
public int val;
public ListNode next;
public ListNode (int x)
{
val = x;
}
}
总共三种解法:
方法一,蛮力法
在第一个链表上顺序遍历每个节点,每遍历到一个节点,就在第二个链表上顺序遍历每个节点。如果在第二个链表上有一个节点和第一个链表上的节点相同,则说明两个链表在这个节点上重合。
如果第一个链表的长度为m,第二个链表的长度为n,那么该方法的时间复杂度为O(nm)。
方法二,辅助栈法
经过观察可以发现,如果两个链表有公共节点,那么公共节点出现在两个链表的尾部。如果我们从两个链表的尾部开始往前比较,那么最后一个相同的节点就是我们要找的节点。
在单链表中,只能从头节点开始按顺序遍历,最后到达尾节点。最后的节点却要最先比较,符合“后进先出”的规律。
所以可以通过栈的特点来解决这个问题:分别把两个链表的节点放入两个栈里,接下来比较两个栈的栈顶是否相同,如果相同,则把栈顶弹出来接着比较下一个栈顶元素,直到找到最后一个相同的元素。
上述思路中,需要两个辅助栈。如果两个链表的长度分别为m和n,空间复杂度是O(m+n),时间复杂度也是O(m+n),和最开始的蛮力法比起来,相当于用空间消耗换取了时间效率。
方法三,先保证两链表剩余相同个节点再遍历查找法
首先遍历两个链表,得到他们的长度,就能知道哪个链表长以及长的链表比短的链表长多少,让长的链表先走若干步,接着同时在两个链表上遍历,找到的第一个相同的节点就是它们的第一个公共节点。
接下来为方法三的AC代码:
using System;
namespace 两个链表的第一个公共节点
{
public class ListNode
{
public int val;
public ListNode next;
public ListNode(int x)
{
val = x;
}
}
class Program
{
static void Main(string[] args)
{
Test1();
}
private static void Test1()
{
ListNode list1 = null, list2 = null;
CreateList(ref list1, ref list2);
Solution s = new Solution();
Console.WriteLine(s.FindFirstCommonNode(list1, list2).val);
Console.WriteLine("链表1的所有节点:");
while (list1 != null)
{
Console.Write(list1.val + "\t");
list1 = list1.next;
}
Console.WriteLine("\n链表2的所有节点:");
while (list2 != null)
{
Console.Write(list2.val + "\t");
list2 = list2.next;
}
}
/// <summary>
/// 创建两个链表
/// </summary>
/// <param name="list1">链表1的头指针</param>
/// <param name="list2">链表2的头指针</param>
static void CreateList(ref ListNode list1, ref ListNode list2)
{
list1 = new ListNode(1);
list2 = new ListNode(5);
ListNode pNode1 = list1, pNode2 = list2;
ListNode temp;
pNode1.next = new ListNode(3);
pNode1 = pNode1.next;
pNode1.next = new ListNode(9);
pNode1 = pNode1.next;
pNode1.next = new ListNode(7);
pNode1 = pNode1.next;
pNode1.next = new ListNode(2);
pNode1 = pNode1.next;
pNode2.next = new ListNode(4);
pNode2 = pNode2.next;
pNode2.next = new ListNode(7);
pNode2 = pNode2.next;
for (int i = 1; i < 3; i++)
{
temp = new ListNode(i + 10);
pNode1.next = temp;
pNode2.next = temp;
pNode1 = pNode1.next;
pNode2 = pNode2.next;
}
}
}
class Solution
{
/// <summary>
/// 输入两个链表,找出它们的第一个公共结点。
/// </summary>
/// <param name="pHead1">链表1的头指针</param>
/// <param name="pHead2">链表2的头指针</param>
/// <returns>第一个公共结点</returns>
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2)
{
if (pHead1 == null || pHead2 == null)
return null;
//链表1的长度和链表2的长度
int list1Length = 0, list2Length = 0;
//链表1的头指针和链表2的头指针
ListNode pNode1 = pHead1, pNode2 = pHead2;
//统计链表1和链表2的长度
while (pNode1 != null)
{
list1Length++;
pNode1 = pNode1.next;
}
while (pNode2 != null)
{
list2Length++;
pNode2 = pNode2.next;
}
pNode1 = pHead1;
pNode2 = pHead2;
//得到链表1和链表2的长度差
int subOfList1BetweenList2 = list1Length - list2Length;
//让长的链表1往后移动若干位
while (subOfList1BetweenList2 < 0)
{
pNode2 = pNode2.next;
subOfList1BetweenList2++;
}
while (subOfList1BetweenList2 > 0)
{
pNode1 = pNode1.next;
subOfList1BetweenList2--;
}
while (pNode1 != pNode2)
{
pNode1 = pNode1.next;
pNode2 = pNode2.next;
}
return pNode1;
}
}
}
从下图的运行结果可以看出,找到了第一个公共节点:
第三种思路和第二种思路比起来,时间复杂度都是O(m+n),但是不再需要辅助栈,因此提高了空间效率。