描述:输入两个无环的单向链表,找出它们的第一个公共子节点,没有公共节点则返回空。
如图A,B两个单链表,我们需要找出它们的第一个公共子节点6
方法一: 没有其他思路时,我们可能会这么做,暴力循环,将节点一一对比,但时间复杂度高,效率太低,不推荐。
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode temp1 = pHead1;
for (; temp1 != null; temp1 = temp1.next) {
ListNode temp2 = pHead2;
for (; temp2 != null; temp2 = temp2.next) {
if (temp1 == temp2) {
return temp1;
}
}
}
return null;
}
}
方法二:使用HashSet集合,将其中某一个单链表放到HashSet(不允许有重复元素)中,再将另一个单链表与HashSet中的元素对比,其自带的contains()方法直接调用来判断是否包含该元素,简单高效。同理HashMap也行。
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
HashSet<ListNode> hs = new HashSet<ListNode>();
while (pHead1 != null) {
hs.add(pHead1);
pHead1 = pHead1.next;
}
while (pHead2 != null) {
if (hs.contains(pHead2))
return pHead2;
pHead2 = pHead2.next;
}
return null;
}
}
方法三:栈,将两个单链表全部压入栈中,如果栈顶元素一样则都执行出栈操作,并将出栈后的节点赋值给resultNode,不一样时则停止,最后一个出栈的元素即为公共节点,交给resultNode返回。如果不存在公共节点,没有出栈操作,那么直接返回的resultNode就是null。
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
Stack<ListNode> s1 = new Stack<>();
Stack<ListNode> s2 = new Stack<>();
while (pHead1 != null) {
s1.push(pHead1);
pHead1 = pHead1.next;
}
while (pHead2 != null) {
s2.push(pHead2);
pHead2 = pHead2.next;
}
ListNode resultNode = null;
while (s1.size() > 0 && s2.size() > 0) {
if (s1.peek() == s2.peek()) {
resultNode = s1.pop();
s2.pop();
} else {
break;
}
}
return resultNode;
}
}
可以参考这张图,理解一下
方法四:拼接两个单链表,由图可知,拼接后的AB,BA链表一定会出现相同的部分,从最后的6开始。我们只需要一边遍历两个拼接后的链表,一边进行比较,就能找到公共节点。
由于新建立链表太浪费空间了,我们可以优化一下。只要每次遍历完一个链表之后,调整一下指针到下一个链表的表头就行了。比如:我们访问完了A之后将指针指向B的表头,再将B遍历完,不就相当于是遍历完了AB。
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
//如果两个单链表中的一个或全部为空,则不可能存在公共节点,直接返回null
return null;
}
ListNode temp1 = pHead1;
ListNode temp2 = pHead2;
while (temp1 != temp2) {
temp1 = temp1.next;
temp2 = temp2.next;
//这个判断条件很重要,如果两个单链表不存在公共节点,那么执行到最后,temp1和temp2都等于null,它们相等了就不会执行下面的语句,同时也会结束while循环,避免了死循环
if (temp1 != temp2) {
//如果第一个链表遍历完了将指针指向第二个的表头
if (temp1 == null) {
temp1 = pHead2;
}
//如果第二个链表遍历完了将指针指向第一个的表头
if (temp2 == null) {
temp2 = pHead1;
}
}
}
return temp1;
//返回temp1或temp2中的那个都行,如果两个单链表不存在公共节点,两个都为null,则返回null。存在公共节点则它们都等于公共节点。
}
}
方法五:通过两个单链表的长度差求公共节点,首先我们分别求出两个单链表的长度设La和Lb,然后我们求| La-Lb |的差值,让长的那个单链表先走| La-Lb |步,最后再一起遍历两个单链表,就能找到公共节点了。
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
int l1 = 0, l2 = 0;
ListNode current1 = pHead1;
ListNode current2 = pHead2;
while (current1 != null) {
current1 = current1.next;
l1++;
}
while (current2 != null) {
current2 = current2.next;
l2++;
}
int sub = (l1 > l2) ? l1 - l2 : l2 - l1;
if (l1 > l2) {
for (int a = 0; a < sub; a++) {
pHead1 = pHead1.next;
}
} else {
for (int a = 0; a < sub; a++) {
pHead2 = pHead2.next;
}
}
while (pHead1 != pHead2) {
pHead1 = pHead1.next;
pHead2 = pHead2.next;
}
return pHead1;
}
}