地址https://leetcode.cn/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode currA = headA;
while (currA != null) {
ListNode currB = headB;
while (currB != null) {
if (currA == currB) {
return currA;
}
currB = currB.next;
}
currA = currA.next;
}
return null;
}
运行截图
时间复杂度:O(mn),两个链表遍历的长度相乘
空间复杂度:O(1),使用了两个节点
可以看出时间复杂度还是很高的
public static ListNode getIntersectionNode2(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
// 先获取相应链表的长度
ListNode currentA = headA;
ListNode currentB = headB;
int lengthA = 0;
int lengthB = 0;
while (currentA != null) {
currentA = currentA.next;
lengthA++;
}
while (currentB != null) {
currentB = currentB.next;
lengthB++;
}
int minLength = lengthA > lengthB ? lengthB : lengthA;
int count = 0;
// 链表A比较短
if (minLength == lengthA) {
while (lengthB - count > lengthA) {
headB = headB.next;
count++;
}
while (headA != headB) {
headB = headB.next;
headA = headA.next;
}
return headA;
}
if (minLength == lengthB) {
while (lengthA - count > lengthB) {
headA = headA.next;
count++;
}
while (headA != headB) {
headB = headB.next;
headA = headA.next;
}
return headB;
}
return null;
}
时间复杂度:首先各自遍历链表长度,为O(m+n), 然后调整长链表的长度和短链表一致,时间复杂度是O(max(m,n)),最后再执行遍历找到公共节点,O(min(m,n)),总的来说O(m+n+max(m,n)+min(m,n)),也不知道算的对不对,但是就是很复杂。
空间复杂度:O(1)两个节点;
总结:虽然一开始感觉时间耗费高,但是测试下来,发现时间耗费这么低。
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Set<ListNode> set = new HashSet<>();
while (headA != null) {
set.add(headA);
headA = headA.next;
}
while (headB != null) {
if (set.contains(headB)) {
return headB;
} else {
headB = headB.next;
}
}
return null;
}
时间复杂度:第一次循环添加headA节点,O(m),第二次遍历headB,O(n),总体O(m+n)
空间复杂度: 使用set来存储节点,O(m)
总结:需要多加注意下尤其是涉及到是否包含相同的节点,数的时候就考虑使用哈希
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Stack<ListNode> stackA = new Stack<ListNode>();
Stack<ListNode> stackB = new Stack<ListNode>();
while (headA != null) {
stackA.add(headA);
headA = headA.next;
}
while (headB != null) {
stackB.add(headB);
headB = headB.next;
}
ListNode preNode = null;
while (stackA.size() > 0 && stackB.size() >0) {
if (stackA.peek() == stackB.peek()) {
preNode = stackA.pop();
stackB.pop();
} else {
break;
}
}
return preNode;
}
运行截图
时间复杂度:第一个链表添加至栈,O(m),第二个链表添加至栈,O(n),最坏情况下O(min(m,n)),就是两个链表的长度谁短,总的O(m+n+min(m,n))
空间复杂度:O(m+n)
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// 需要考虑不为空的情况
if (headA == null || headB == null) {
return null;
}
ListNode p1 = headA;
ListNode p2 = headB;
while (p1 != p2) {
// 不为null的时候继续遍历
p1 = p1.next;
p2 = p2.next;
if (p1 != p2) {
// 链表1结束,转为链表2
if (p1 == null) {
p1 = headB;
}
if (p2 == null) {
p2 = headA;
}
}
}
return p1;
}
时间复杂度:最坏O(m+n)没有节点
空间复杂度:O(1)
总结:里面的两个if是没有关系的,不能使用else。假设链表1大于链表2的长度,那么第一个拼接的链表,公共点就在拼接的链表2上面,第二个拼接的链表,公共点就在链表1上面。核心思路还是将链表长度归一。
ListNode p1 = headA;
ListNode p2 = headB;
int lengthA = 0;
int lengthB = 0;
while (p1 != null) {
lengthA++;
p1 = p1.next;
}
while (p2 != null) {
lengthB++;
p2 = p2.next;
}
p1 = headA;
p2 = headB;
// 判断长度
int sub = lengthA > lengthB ? lengthA - lengthB : lengthB - lengthA;
if (lengthA>lengthB){
int a =0;
while (a < sub){
p1 = p1.next;
a++;
}
}
if (lengthB>lengthA){
int b =0;
while (b<sub){
p2 = p2.next;
b++;
}
}
while (p1!=p2){
p1 =p1.next;
p2=p2.next;
}
return p1;
}
运行截图
时间复杂度:两个链表遍历长度O(m+n),判断sub谁小O(sub),同步找到公共节点O(min(m,n)),总的O(m+n+min(m,n)+sub);
空间复杂度:O(1)
总结: 相比于之前的代码这个还节省了内存。
其中有3个方法都是围绕着链表的长度来的,很明显,这道题目核心就是链表长度。