面试题 02.07. 链表相交
同:160.链表相交
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
示例 1:
示例 2:
示例 3:
思路
简单来说,就是求两个链表交点节点的指针。 这里同学们要注意,交点不是数值相等,而是指针相等。
为了方便举例,假设节点元素数值相等,则节点指针相等。
看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点:
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
否则循环退出返回空指针。
C++代码如下:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
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) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
- 时间复杂度:O(n + m)
- 空间复杂度:O(1)
其他语言版本
Java
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
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;
}
}
Python
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA, lenB = 0, 0
cur = headA
while cur: # 求链表A的长度
cur = cur.next
lenA += 1
cur = headB
while cur: # 求链表B的长度
cur = cur.next
lenB += 1
curA, curB = headA, headB
if lenA > lenB: # 让curB为最长链表的头,lenB为其长度
curA, curB = curB, curA
lenA, lenB = lenB, lenA
for _ in range(lenB - lenA): # 让curA和curB在同一起点上(末尾位置对齐)
curB = curB.next
while curA: # 遍历curA 和 curB,遇到相同则直接返回
if curA == curB:
return curA
else:
curA = curA.next
curB = curB.next
return None
Go
func getIntersectionNode(headA, headB *ListNode) *ListNode {
curA := headA
curB := headB
lenA, lenB := 0, 0
// 求A,B的长度
for curA != nil {
curA = curA.Next
lenA++
}
for curB != nil {
curB = curB.Next
lenB++
}
var step int
var fast, slow *ListNode
// 请求长度差,并且让更长的链表先走相差的长度
if lenA > lenB {
step = lenA - lenB
fast, slow = headA, headB
} else {
step = lenB - lenA
fast, slow = headB, headA
}
for i:=0; i < step; i++ {
fast = fast.Next
}
// 遍历两个链表遇到相同则跳出遍历
for fast != slow {
fast = fast.Next
slow = slow.Next
}
return fast
}
双指针
func getIntersectionNode(headA, headB *ListNode) *ListNode {
l1,l2 := headA, headB
for l1 != l2 {
if l1 != nil {
l1 = l1.Next
} else {
l1 = headB
}
if l2 != nil {
l2 = l2.Next
} else {
l2 = headA
}
}
return l1
}
javaScript
var getListLen = function(head) {
let len = 0, cur = head;
while(cur) {
len++;
cur = cur.next;
}
return len;
}
var getIntersectionNode = function(headA, headB) {
let curA = headA,curB = headB,
lenA = getListLen(headA), // 求链表A的长度
lenB = getListLen(headB);
if(lenA < lenB) { // 让curA为最长链表的头,lenA为其长度
// 交换变量注意加 “分号” ,两个数组交换变量在同一个作用域下时
// 如果不加分号,下面两条代码等同于一条代码: [curA, curB] = [lenB, lenA]
[curA, curB] = [curB, curA];
[lenA, lenB] = [lenB, lenA];
}
let i = lenA - lenB; // 求长度差
while(i-- > 0) { // 让curA和curB在同一起点上(末尾位置对齐)
curA = curA.next;
}
while(curA && curA !== curB) { // 遍历curA 和 curB,遇到相同则直接返回
curA = curA.next;
curB = curB.next;
}
return curA;
};
TypeScript:
function getIntersectionNode(headA: ListNode | null, headB: ListNode | null): ListNode | null {
let sizeA: number = 0,
sizeB: number = 0;
let curA: ListNode | null = headA,
curB: ListNode | null = headB;
while (curA) {
sizeA++;
curA = curA.next;
}
while (curB) {
sizeB++;
curB = curB.next;
}
curA = headA;
curB = headB;
if (sizeA < sizeB) {
[sizeA, sizeB] = [sizeB, sizeA];
[curA, curB] = [curB, curA];
}
let gap = sizeA - sizeB;
while (gap-- && curA) {
curA = curA.next;
}
while (curA && curB) {
if (curA === curB) {
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
};
C:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *l = NULL, *s = NULL;
int lenA = 0, lenB = 0, gap = 0;
// 求出两个链表的长度
s = headA;
while (s) {
lenA ++;
s = s->next;
}
s = headB;
while (s) {
lenB ++;
s = s->next;
}
// 求出两个链表长度差
if (lenA > lenB) {
l = headA, s = headB;
gap = lenA - lenB;
} else {
l = headB, s = headA;
gap = lenB - lenA;
}
// 尾部对齐
while (gap--) l = l->next;
// 移动,并检查是否有相同的元素
while (l) {
if (l == s) return l;
l = l->next, s = s->next;
}
return NULL;
}
Scala:
object Solution {
def getIntersectionNode(headA: ListNode, headB: ListNode): ListNode = {
var lenA = 0 // headA链表的长度
var lenB = 0 // headB链表的长度
var tmp = headA // 临时变量
// 统计headA的长度
while (tmp != null) {
lenA += 1;
tmp = tmp.next
}
// 统计headB的长度
tmp = headB // 临时变量赋值给headB
while (tmp != null) {
lenB += 1
tmp = tmp.next
}
// 因为传递过来的参数是不可变量,所以需要重新定义
var listA = headA
var listB = headB
// 两个链表的长度差
// 如果gap>0,lenA>lenB,headA(listA)链表往前移动gap步
// 如果gap<0,lenA<lenB,headB(listB)链表往前移动-gap步
var gap = lenA - lenB
if (gap > 0) {
// 因为不可以i-=1,所以可以使用for
for (i <- 0 until gap) {
listA = listA.next // 链表headA(listA) 移动
}
} else {
gap = math.abs(gap) // 此刻gap为负值,取绝对值
for (i <- 0 until gap) {
listB = listB.next
}
}
// 现在两个链表同时往前走,如果相等则返回
while (listA != null && listB != null) {
if (listA == listB) {
return listA
}
listA = listA.next
listB = listB.next
}
// 如果链表没有相交则返回null,return可以省略
null
}
}