Day36:两个链表的第一个公共结点

时间:2022-07-24
本文章向大家介绍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!!!

参考文献

[1] 挪罗儿 [2] 有理想的番茄