链表专题——链表的公共子节点问题(java实现)
问题:输入两个链表,找出它们的第一个公共子节点(注意:是结点相同不仅仅是是结点里的值相同)
首先依旧是链表的定义:(leetcode方式)
class ListNode {
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
next = null;
}
}
方法一:使用栈
将两个链表的所有元素从头到尾依次入栈,再依次出栈进行挨个对照,最后一次相同的结点就是我们要找的结点
public static ListNode findFristCommonNodeByStack(ListNode l1, ListNode l2) {
if(l1 == null || l2 == null) {
return null;
}
Stack<ListNode> stack1 = new Stack<>();
Stack<ListNode> stack2 = new Stack<>();
ListNode head1 = l1;
ListNode head2 = l2;
while(head1!= null) {
stack1.push(head1);
head1 = head1.next;
}
while(head2!= null) {
stack2.push(head2);
head2 = head2.next;
}
ListNode findNode = null;
while(stack1.size() > 0 && stack2.size() > 0) {
if(stack1.peek() == stack2.peek()) {
findNode = stack1.pop();
stack2.pop();
}else {
break;
}
}
return findNode;
}
方法二:使用集合
将其中一个链表的所有元素都放到set中,再遍历另一个链表,查看是否有集合中的元素,有的话直接返回;同理也可以使用哈希
public static ListNode findFristCommonNodeBySet(ListNode l1, ListNode l2) {
if(l1 == null || l2 == null) {
return null;
}
Set<ListNode> set = new HashSet<>();
while (l1 != null) {
set.add(l1);
l1 = l1.next;
}
while (l2 != null) {
if(set.contains(l2)) {
return l2;
}
l2 = l2.next;
}
return null;
}
可以看出比使用栈的方法简单了很多,而且时间复杂度和空间复杂度都少了一个O(n)
方法三:使用插值和双指针的方式
前面的方法都要额外开辟内存空间,此方法只需要变量即可;具体为:
-
插值:先遍历得到两个链表的长度,再将长度进行相减|L1- L2|=k 得到的k值就是长度差,
-
双指针:一个变量从短链表的开头出发,另外一个先走k个单位,最后两个变量在同时出发,出现相同结点直接返回即可;
public static ListNode findFristCommonNodeByInterpolationDoublePointer(ListNode l1, ListNode l2) {
if(l1 == null || l2 == null) {
return null;
}
int length1 = 0;
int length2 = 0;
ListNode current1 = l1;
ListNode current2 = l2;
while(current1 != null) {
length1++;
current1 = current1.next;
}
while(current2 != null) {
length2++;
current2 = current2.next;
}
current1 = l1;
current2 = l2;
int interpolation = length1 - length2;
if(interpolation > 0) {
for (int i = 0; i < interpolation; i++) {
current1 = current1.next;
}
}else {
interpolation = Math.abs(interpolation);
for (int i = 0; i < interpolation; i++) {
current2 = current2.next;
}
}
while(current1 != current2) {
current1 = current1.next;
current2 = current2.next;
}
return current1;
}
初始化题目链表的方法
private static ListNode[] initLinkedList() {
ListNode[] heads = new ListNode[2];
// 构造第一个链表交点之前的元素 1 ->2-> 3
heads[0] = new ListNode(1);
ListNode current1 = heads[0];
current1.next = new ListNode(2);
current1 = current1.next;
current1.next = new ListNode(3);
current1 = current1.next;
// 11->22
// 构造第二个链表交点之前的元素
heads[1] = new ListNode(11);
ListNode current2 = heads[1];
current2.next = new ListNode(22);
current2 = current2.next;
// 构造公共交点以及之后的元素
ListNode node4 = new ListNode(4);
current1.next = node4;
current2.next = node4;
ListNode node5 = new ListNode(5);
node4.next = node5;
ListNode node6 = new ListNode(6);
node5.next = node6;
return heads;
}
}
进行测试:
public class FirstCommonNode {
public static void main(String[] args) {
ListNode[] heads = initLinkedList();
//l1 为 1 2 3 4 5
//l2 为 11 22 4 5
ListNode l1 = heads[0];
ListNode l2 = heads[1];
ListNode find = null;
//对三种方法进行测试
find = findFristCommonNodeByStack(l1, l2);
System.out.println(find.val);
find = findFristCommonNodeBySet(l1, l2);
System.out.println(find.val);
find = findFristCommonNodeByInterpolationDoublePointer(l1, l2);
System.out.println(find.val);
}
运行结果: