Day36:两个链表的第一个公共结点
剑指Offer_编程题——两个链表的第一个公共结点
题目描述:
输入两个链表,找出他们的第一个公共结点(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)。
具体要求:
时间限制: C/C++ 1秒,其他语言2秒 空间限制: C/C++32M,其他语言64M
具体思路:
思路一: 刚看到这道题的时候,我们瞬间想到了前面做过的一道题,前面的题是输出链表中倒数第K个结点。而我们的这道题是输出两个链表的第一个公共结点,其中共同的是,我们都遍历这两个链表,找出其中公共的第一个结点,应该可以这么说这道题是那道题的子集,当时我们看到了一种独特的解法,这种解法很值得我们借鉴,那就是:首先设置两个指针,将他们之间的长度设置为k,这里需要我们注意的是,我们定义的这两个指针是两把尺子,当尺子的末指针指向链表的末尾时,它的首指针就指向第k个结点。我们也可以借鉴这种思路来解决该题。具体应该先各自遍历得到链表长度,让较长的链表先走d步使二者同步,然后两条链表一起走,第一个相同的节点就是公共起始节点。具体用python实现这种思路:
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
p1, p2 = pHead1, pHead2
l1, l2 = 0, 0
while p1 is not None:
l1 += 1
p1 = p1.next
while p2 is not None:
l2 += 1
p2 = p2.next
if l1 > l2:
pShort = pHead2
pLong = pHead1
else:
pShort = pHead1
pLong = pHead2
for _ in range(abs(l1 - l2)):
pLong = pLong.next
for _ in range(min(l1, l2)):
if pShort == pLong:
return pShort
pShort, pLong = pShort.next, pLong.next
return None
代码效果图如图所示:
正如我们之前的文章中提到,如果在牛客网中,我们以上代码可以直接编译通过,因为它给我们定义了节点ListNode,但如果在本地编译器(PyCharm)中,我们得自己定义该节点,具体实现如下:
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
根据代码以及运行结果可知,假设两个链表长度分别为m,n,则这种算法的时间复杂度为O(m+n),空间复杂度为O(1),其算法是最优的。其实我们还有一种与之类似的算法,该算法是:从链表头判断,长链表先走,直至两个链表持平。具体我们用java将其实现:
public class Solution{
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2){
if(pHead1 == null || pHead2 == null)
return null;
int len1 = 0, len2 = 0;
ListNode n1 = pHead1, n2 = pHead2;
while(n1 != null || n2 != null){
if(n1 != null){
len1++;
n1 = n1.next;
}
if(n2 != null){
len2++;
n2 = n2.next;
}
}
int distance = Math.abs(len1 - len2);
while(pHead1 != null || pHead2 != null){
while(distance --> 0){
if(len1 > len2)
pHead1 = pHead1.next;
else
pHead2 = pHead2.next;
}
if(pHead1 == pHead2){
return pHead1;
}else{
pHead1 = pHead1.next;
pHead2 = pHead2.next;
}
}
return null;
}
}
代码效果图如图所示:
用java实现ListNode类:
public class ListNide{
int val;
ListNode next = null;
ListNode(int val){
this.val = val;
}
}
思路二 我们也可以用java容器中的HashMap来处理该题。具体用java实现如下:
import java.util.HashMap;
public class Solution{
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2){
HashMap<ListNode, Integer> map = new HashMap<>();
ListNode p1 = pHead1;
while(p1 != null){
map.put(p1, 1);
p1 = p1.next;
}
while(pHead2 != null){
if(map.containsKey(pHead2))
return pHead2;
pHead2 = pHead2.next;
}
return null;
}
}
代码效果图如图所示:
思路三 两个链表形成一个Y字型,考虑从链表尾开始比较,最后一个相等的节点是起始公共节点。根据“后进先出”的思路,分别遍历两条链表,用2个栈存储链表节点。然后从栈顶元素开始比较。这个“Y”字型的链表如下图所示:
具体用python实现如下:
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
stack1, stack2 = [], []
p1, p2 = pHead1, pHead2
while p1 is not None:
stack1.append(p1)
p1 = p1.next
while p2 is not None:
stack2.append(p2)
p2 = p2.next
commonde = None
while stack1 and stack2:
p1, p2 = stack1.pop(), stack2.pop()
if p1 != p2:
return commonde
commonde = p1
return commonde
代码效果图如图所示:
根据代码以及运行结果可知,假设两个链表长度分别为m,n,c是公共链表长度,则这种算法的时间复杂度为O(m+n+c),空间复杂度为O(m+n)。其实如果没有看过之前的文章的,可以有一种最简单、最暴力的解法,对于p1的每个节点,遍历p2节点,逐一判断两节点是否相等。具体用python实现如下:
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
p1 = pHead1
while p1:
p2 = pHead2
while p2:
if p1 == p2:
return p1
p2 = p2.next
p1 = p1.next
return None
代码效果图如图所示:
根据代码以及运行结果可知,假设两个链表长度分别为m,n,,则这种算法的时间复杂度为O(mn),空间复杂度为O(1)。以上就是关于这道题的全部解答。
总结
本道题主要考察两个链表出现的第一个公共结点,正如我们在刚开始说的,其实之前有过类似的题目,因此解起来并不会过于复杂,我们分别用了我三种思路将其实现,并且分别分析了其中的时间复杂度和空间复杂度。首先我们第一个想到的思路就是和之前我们做的那种典型思路类似,我们用python将其实现,另外,我们用了一种差不多思路一的方法用java将其实现,后面又通过java中的HashMap来将该问题解决,以及利用链表输出“Y”字型,通过两个栈来将其实现,最后就介绍了一种简单粗暴、什么知识点也不用考虑,既然题目要求找到第一个公共结点,那就直接对比两个节点,直到遇到两个相同的节点即可。。因此,我们在做题的时候,应该多次尝试各种方法,扩展自己的思维,写出优质的代码。总之,我们要继续加油,争取早日找到工作,Good Luck!!!
参考文献
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Ubuntu安装Redis(手动编译)
- 第五节:Activiti6.0——流程定义相关API
- 第四节:Activiti6.0——使用zip、bpmn代码生成流程图的资源部署以及资源的查询
- 第三节:Activiti6.0——Query的API使用
- 第二节:Activiti6.0配置文件的读取
- SpringMvc配置文件上传
- 使用web.xml配置SpringMvc(使用Java加载配置)
- SpringMVC配置字符过滤器的两种方式
- SpringMvc使用Json(Java配置方式)
- 使用IDEA创建SpringMvc并整合Thymeleaf(Java配置版)
- Junit报错:Argument(s) are different! Wanted:
- RocketMQ使用过滤器filterServer(4.3版本后)
- Python+Appium运行简单的demo,你需要理解Appium运行原理!
- 网站日志分析完整实践【技术创造101训练营】
- HTML前端知识