1 解决两个链表的第一个公共子结点
本文主要看一道LeetCode经典的链表题目。剑指offer 52题 :输入两个链表,找出它们的第一个公共节点。例如下面的两个链表:
两个链表的头结点都是已知的,相交后成为一个单链表,但是相交的位置未知,并且相交之前的结点数也是未知的,请设计算法找到两个链表的合并点。
1.1 分析
- 没有思路时的解题方法
将常用的数据结构和常用算法思想想一遍,看看哪些能解决问题。
常用的数据结构有数组、链表、队、栈、Hash、集合、树、堆。常用的算法思想有查找、排序、双指针、递归、迭代、分治、贪心、回溯和动态规划等等。
1.2 拼接两个字符串
尝试把上面的链表分别拼接成AB和BA,可以得到下图结果。有意思的是,从最后的c1开始,两个链表开始一样了,c1就是要找的公共结点,所以可以通过拼接的方式寻找交点。
那要怎么做呢?分别遍历AB和BA进行比较,这样还要重新建立新的链表浪费空间,为进一步优化,可以在每个链表访问完之后,调整一下链表的表头继续遍历即可。
C语言实现
struct ListNode *getCommNode(struct ListNode *headA, struct ListNode *headB)
{
if (headA == NULL || headB == NULL)
{
return NULL;
}
ListNode *p1 = headA;
ListNode *p2 = headB;
while (p1 != p2)
{
p1 = p1->next;
p2 = p2->next;
if (p1 != p2) // 防止A和B无交集时陷入死循环
{
if (p1 == NULL)
{
p1 = headB;
}
if (p2 == NULL)
{
p2 = headA;
}
}
}
return p1;
}
1.3 差和双指针
假如两个链表一定存在公共结点,假设链表A的长度为L1,链表B的长度为L2,那么,| L1-L2 |就是两个的差值。遍历的时候,长的链表先走| L1-L2 |,然后两个链表同时向前走,结点一样的时候就是公共结点。
那要怎么做呢,首先遍历第一遍获取两个链表长度;然后,计算两个链表的差值,让长的先走差值的步长;最后再一一进行比较,找到相同的结点。
C语言实现
struct ListNode *getCommList(struct ListNode *headA , struct ListNode *headB)
{
struct ListNode *p1 = headA;
struct ListNode *p2 = headB;
int len1 = 0;
int len2 = 0;
int lenSub = 0;
if(headA == NULL || headB == NULL)
{
return NULL;
}
while (p1)
{
len1++;
p1 = p1->next;
}
while (p2)
{
len2++;
p2 = p2->next;
}
p1 = headA;
p2 = headB;
lenSub = len1 > len2 ? len1 - len2 : len2 - len1;
if(len1>len2)
{
int count1 = 0;
while( count1 != lenSub)
{
p1 = p1->next;
count1++;
}
}
if(len1<len2)
{
int count2 = 0;
while(count2 != lenSub)
{
p2 = p2->next;
count2++;
}
}
while (p1!=p2)
{
p1 = p1->next;
p2 = p2->next;
}
return p1;
};