问题:
如图,观察链表A和链表B,到C1节点开始相交,后面相交后成为一个单链表,已知头结点,相交位置未知,设计算法找到两个链表的合并点(即第一个相交点)
注意:
创建这连个链表时,不要像我一样,创建成这样,下图是错误的
创建两个链表的代码:
private static Node[] initLinkedList() {
Node[] heads = new Node[2];
// 构造第一个链表交点之前的元素 1 ->2-> 3
heads[0] = new Node(1);
Node current1 = heads[0];
current1.next = new Node(2);
current1 = current1.next;
current1.next = new Node(3);
current1 = current1.next;
// 11->22
// 构造第二个链表交点之前的元素
heads[1] = new Node(11);
Node current2 = heads[1];
current2.next = new Node(22);
current2 = current2.next;
// 构造公共交点以及之后的元素
Node node4 = new Node(4);
current1.next = node4;
current2.next = node4;
Node node5 = new Node(5);
node4.next = node5;
Node node6 = new Node(6);
node5.next = node6;
return heads;
}
方式一:双层循环判断
思路分析:
首先能想到的就是双层循环判断,一个节点一个节点比较,类似于冒泡排序,容易想到但是时间复杂度太高
方法二:使用hashSet和hashMap
思路分析:
使用hashSet或者hashMap,把链表A的每个节点都放入hashSet中,或者hashMap(key为节点,值为null)中,循环链表B的每个节点,并判断该节点是否在hashSet中,存在则return 该节点,后边的直接不判断了(因为找到就是第一个相交点)
代码实现:
public static Node getCommonNode(Node nodeA, Node nodeB) {
Set<Node> set = new HashSet<>();
while (nodeA != null){
set.add(nodeA);
nodeA = nodeA.next;
}
while (nodeB != null){
boolean isContains = set.contains(nodeB);
if(isContains)
return nodeB;
nodeB = nodeB.next;
}
return null;
}
方法三:
思路分析:
使用栈,把链表A和链表B的每个节点都分别放在stackA和stackB中,会把链表的第一个节点放在栈底,最后一个节点放在栈顶,当两个栈都不是空时,比较两个栈顶元素是否相等,相等就给赋值给commNode节点并都出栈,直到两个栈顶元素都不相同,即break退出循环,并return commNode节点
代码实现:
public static Node getCommonNodeByStark(Node nodeA, Node nodeB) {
if (nodeA == null || nodeB == null){
return null;
}
Stack<Node> stackA = new Stack<>();
Stack<Node> stackB = new Stack<>();
while (nodeA != null){
stackA.push(nodeA);
nodeA = nodeA.next;
}
while (nodeB != null){
stackB.push(nodeB);
nodeB = nodeB.next;
}
Node commNode = null;
while(!stackA.empty() && !stackB.empty()){
if(stackA.peek() == stackB.peek()){
commNode=stackA.pop();
stackB.pop();
}else{
break;
}
}
return commNode;
}
方法四:链表相加
思路分析:
使用字符串拼接的方式
优化:新建链表太麻烦,当链表A为null时,把链表B的头结点赋值给A ,就构成了链表AB,链表BA同理
思路分析:
什么时候链表AB会和链表BA相同
1、最后一个相交点
2、当两个链表没有相交点,链表AB和BA都循环结束,都为null时,也会相等,所以循环体中添加if(pNodeA != pNodeB)的判断,是为了防止无相交点时,出现死循环
代码实现:
public static Node getCommonNodeByStrTogether(Node nodeA, Node nodeB) {
if (nodeA == null || nodeB == null){
return null;
}
Node pNodeA = nodeA;
Node pNodeB = nodeB;
while(pNodeA !=pNodeB){
pNodeA = pNodeA.next;
pNodeB = pNodeB.next;
if(pNodeA != pNodeB){ //是为了防止pNodeA和pNodeB都为null,还赋值
if (pNodeA == null){
pNodeA = nodeB;
}
if (pNodeB == null){
pNodeB = nodeA;
}
}
}
return pNodeA;
}
方式五:长的链表减去差值
思路分析:
长的链表减去两个链表之间的差值,这样就会使两个链表的长度相等,相等之后再在循环中判断哪个节点相同
代码实现:
/**
* 长的链表减去两个链表的差值,之后两个链表的长度就相等了
* @param nodeA
* @param nodeB
* @return
*/
public static Node getCommonNodeBySub(Node nodeA, Node nodeB) {
if (nodeA == null || nodeB == null) {
return null;
}
Node pNodeA = nodeA;
Node pNodeB = nodeB;
int lenA = 0;
int lenB = 0;
while (pNodeA != null) {
lenA++;
pNodeA = pNodeA.next;
}
while (pNodeB != null) {
lenB++;
pNodeB = pNodeB.next;
}
//注意,当遍历链表取长度时,pNodeA和pNodeB会为空,要重新赋值(指向头结点)
pNodeA = nodeA;
pNodeB = nodeB;
int sub = lenA > lenB ? lenA - lenB : lenB - lenA;
if(lenA > lenB){
for (int i = 0; i < sub; i++) {
pNodeA = pNodeA.next;
}
}
if(lenB > lenA){
for (int i = 0; i < sub; i++) {
pNodeB = pNodeB.next;
}
}
while (pNodeA != pNodeB){
pNodeA = pNodeA.next;
pNodeB = pNodeB.next;
}
return pNodeA;
}