力扣题目
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在节点 c1 开始相交。
链表的定义
public class ListNode {
//结点的值
int val;
//下一个结点
ListNode next;
//结点的构造函数 无参
public ListNode() {
}
//结点的构造函数,一个参数
public ListNode(int val) {
this.val = val;
}
}
小技巧:
如果题目刚拿到手的时候没有思路怎么办?
试着将常用的数据结构和常用的算法思想都想一遍,一个一个试,看有没有能解决的。
常用的数据结构:数组、链表、队列、栈、Hash表、集合、树、堆等等
常用的算法:查找、排序、双指针、递归、迭代、分治、回溯和动态规划等等
方法1——哈希和集合
先将一个链表元素全部存到Map里,然后一边遍历第二个链表,一边检测Hash中是否存在当前节点,如果有交点,那么一定能检测出来。对于本题,如果使用集合更合适,而且代码也更简洁。
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ArrayList<ListNode> listNodes = new ArrayList<>();
while (headA != null){
listNodes.add(headA);
headA = headA.next;
}
while (headB != null){
if (listNodes.contains(headB)){
return headB;
}
headB = headB.next;
}
return null;
}
方法2——栈
使用两个栈,分别将两个链表的结点入两个栈,然后分别出栈,如果相等就继续出栈,一直找到最晚出栈的那一组。需要两个O(n)的空间
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Stack<ListNode> stackA = new Stack<>();
Stack<ListNode> stackB = new Stack<>();
while (headA != null) {
stackA.push(headA);
headA = headA.next;
}
while (headB != null) {
stackB.push(headB);
headB = headB.next;
}
ListNode preNode = null;
while (stackB.size() > 0 && stackA.size() > 0){
if (stackA.peek() == stackB.peek()){
preNode = stackA.pop();
stackB.pop();
}else {
break;
}
}
return preNode;
}
方法3——拼接两个字符串
先看下面的链表A和B:
A: 0-1-2-3-4-5
B: a-b-4-5
分别拼接成AB和BA会怎么样?
AB:0-1-2-3-4-5-a-b-4-5
BA:a-b-4-5-0-1-2-3-4-5
这样就发现,从最后的4开始,两个链表就是一样的,所以4就是要找的结点。
如图所示:
另:为减少空间复杂度,只需要在遍历完A之后,接着遍历B。同样遍历B,在遍历A。就不需要新建两个链表了。
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode curA = headA;
ListNode curB = headB;
while (curA != curB) {
curA = curA.next;
curB = curB.next;
//一个链表访问完了,就跳到另一个链表继续访问
if (curA == null) {
curA = curB;
}
if (curB == null) {
curB = curA;
}
}
return curA;
}
方法4——差和双指针
求两个链表交点结点的指针。注意:交点不是数值相等,而是指针相等。
求出两个链表的长度,在求出两个链表的差值。然后让curA移动到和curB末尾对其的位置,如图:
此时就可以比较curA和curB是否相同,如果不同,同是向后移动curA和curB,如果遇到curA== curB,则找到交点。
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ArrayList<ListNode> listNodes = new ArrayList<>();
while (headA != null){
listNodes.add(headA);
headA = headA.next;
}
while (headB != null){
if (listNodes.contains(headB)){
return headB;
}
headB = headB.next;
}
return null;
ListNode curA = headA;
ListNode curB = headB;
int lenA = 0, lenB = 0;
while (curA != null) { // 求链表A的长度
lenA++;
curA = curA.next;
}
while (curB != null) { // 求链表B的长度
lenB++;
curB = curB.next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
//1. swap (lenA, lenB);
int tmpLen = lenA;
lenA = lenB;
lenB = tmpLen;
//2. swap (curA, curB);
ListNode tmpNode = curA;
curA = curB;
curB = tmpNode;
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap-- > 0) {
curA = curA.next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != null) {
if (curA == curB) {
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}