面试题02.07.链表相交 - 图1
参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

面试题 02.07. 链表相交

力扣题目链接

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

面试题02.07.链表相交 - 图2

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:

面试题02.07.链表相交 - 图3

示例 2:

面试题02.07.链表相交 - 图4

示例 3:

面试题02.07.链表相交 - 图5面试题02.07.链表相交 - 图6

思路

简单来说,就是求两个链表交点节点的指针。 这里同学们要注意,交点不是数值相等,而是指针相等。

为了方便举例,假设节点元素数值相等,则节点指针相等。

看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点:

面试题02.07.链表相交 - 图7

我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:

面试题02.07.链表相交 - 图8

此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。

否则循环退出返回空指针。

C++代码如下:

  1. class Solution {
  2. public:
  3. ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
  4. ListNode* curA = headA;
  5. ListNode* curB = headB;
  6. int lenA = 0, lenB = 0;
  7. while (curA != NULL) { // 求链表A的长度
  8. lenA++;
  9. curA = curA->next;
  10. }
  11. while (curB != NULL) { // 求链表B的长度
  12. lenB++;
  13. curB = curB->next;
  14. }
  15. curA = headA;
  16. curB = headB;
  17. // 让curA为最长链表的头,lenA为其长度
  18. if (lenB > lenA) {
  19. swap (lenA, lenB);
  20. swap (curA, curB);
  21. }
  22. // 求长度差
  23. int gap = lenA - lenB;
  24. // 让curA和curB在同一起点上(末尾位置对齐)
  25. while (gap--) {
  26. curA = curA->next;
  27. }
  28. // 遍历curA 和 curB,遇到相同则直接返回
  29. while (curA != NULL) {
  30. if (curA == curB) {
  31. return curA;
  32. }
  33. curA = curA->next;
  34. curB = curB->next;
  35. }
  36. return NULL;
  37. }
  38. };
  • 时间复杂度:面试题02.07.链表相交 - 图9#card=math&code=O%28n%20%2B%20m%29)
  • 空间复杂度:面试题02.07.链表相交 - 图10#card=math&code=O%281%29)

其他语言版本

Java

  1. public class Solution {
  2. public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
  3. ListNode curA = headA;
  4. ListNode curB = headB;
  5. int lenA = 0, lenB = 0;
  6. while (curA != null) { // 求链表A的长度
  7. lenA++;
  8. curA = curA.next;
  9. }
  10. while (curB != null) { // 求链表B的长度
  11. lenB++;
  12. curB = curB.next;
  13. }
  14. curA = headA;
  15. curB = headB;
  16. // 让curA为最长链表的头,lenA为其长度
  17. if (lenB > lenA) {
  18. //1. swap (lenA, lenB);
  19. int tmpLen = lenA;
  20. lenA = lenB;
  21. lenB = tmpLen;
  22. //2. swap (curA, curB);
  23. ListNode tmpNode = curA;
  24. curA = curB;
  25. curB = tmpNode;
  26. }
  27. // 求长度差
  28. int gap = lenA - lenB;
  29. // 让curA和curB在同一起点上(末尾位置对齐)
  30. while (gap-- > 0) {
  31. curA = curA.next;
  32. }
  33. // 遍历curA 和 curB,遇到相同则直接返回
  34. while (curA != null) {
  35. if (curA == curB) {
  36. return curA;
  37. }
  38. curA = curA.next;
  39. curB = curB.next;
  40. }
  41. return null;
  42. }
  43. }

Python

  1. class Solution:
  2. def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
  3. """
  4. 根据快慢法则,走的快的一定会追上走得慢的。
  5. 在这道题里,有的链表短,他走完了就去走另一条链表,我们可以理解为走的快的指针。
  6. 那么,只要其中一个链表走完了,就去走另一条链表的路。如果有交点,他们最终一定会在同一个
  7. 位置相遇
  8. """
  9. cur_a, cur_b = headA, headB # 用两个指针代替a和b
  10. while cur_a != cur_b:
  11. cur_a = cur_a.next if cur_a else headB # 如果a走完了,那么就切换到b走
  12. cur_b = cur_b.next if cur_b else headA # 同理,b走完了就切换到a
  13. return cur_a

Go

  1. func getIntersectionNode(headA, headB *ListNode) *ListNode {
  2. curA := headA
  3. curB := headB
  4. lenA, lenB := 0, 0
  5. // 求A,B的长度
  6. for curA != nil {
  7. curA = curA.Next
  8. lenA++
  9. }
  10. for curB != nil {
  11. curB = curB.Next
  12. lenB++
  13. }
  14. var step int
  15. var fast, slow *ListNode
  16. // 请求长度差,并且让更长的链表先走相差的长度
  17. if lenA > lenB {
  18. step = lenA - lenB
  19. fast, slow = headA, headB
  20. } else {
  21. step = lenB - lenA
  22. fast, slow = headB, headA
  23. }
  24. for i:=0; i < step; i++ {
  25. fast = fast.Next
  26. }
  27. // 遍历两个链表遇到相同则跳出遍历
  28. for fast != slow {
  29. fast = fast.Next
  30. slow = slow.Next
  31. }
  32. return fast
  33. }

javaScript

  1. var getListLen = function(head) {
  2. let len = 0, cur = head;
  3. while(cur) {
  4. len++;
  5. cur = cur.next;
  6. }
  7. return len;
  8. }
  9. var getIntersectionNode = function(headA, headB) {
  10. let curA = headA,curB = headB,
  11. lenA = getListLen(headA),
  12. lenB = getListLen(headB);
  13. if(lenA < lenB) {
  14. // 下面交换变量注意加 “分号” ,两个数组交换变量在同一个作用域下时
  15. // 如果不加分号,下面两条代码等同于一条代码: [curA, curB] = [lenB, lenA]
  16. [curA, curB] = [curB, curA];
  17. [lenA, lenB] = [lenB, lenA];
  18. }
  19. let i = lenA - lenB;
  20. while(i-- > 0) {
  21. curA = curA.next;
  22. }
  23. while(curA && curA !== curB) {
  24. curA = curA.next;
  25. curB = curB.next;
  26. }
  27. return curA;
  28. };

TypeScript:

  1. function getIntersectionNode(headA: ListNode | null, headB: ListNode | null): ListNode | null {
  2. let sizeA: number = 0,
  3. sizeB: number = 0;
  4. let curA: ListNode | null = headA,
  5. curB: ListNode | null = headB;
  6. while (curA) {
  7. sizeA++;
  8. curA = curA.next;
  9. }
  10. while (curB) {
  11. sizeB++;
  12. curB = curB.next;
  13. }
  14. curA = headA;
  15. curB = headB;
  16. if (sizeA < sizeB) {
  17. [sizeA, sizeB] = [sizeB, sizeA];
  18. [curA, curB] = [curB, curA];
  19. }
  20. let gap = sizeA - sizeB;
  21. while (gap-- && curA) {
  22. curA = curA.next;
  23. }
  24. while (curA && curB) {
  25. if (curA === curB) {
  26. return curA;
  27. }
  28. curA = curA.next;
  29. curB = curB.next;
  30. }
  31. return null;
  32. };

C:

  1. ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
  2. ListNode *l = NULL, *s = NULL;
  3. int lenA = 0, lenB = 0, gap = 0;
  4. // 求出两个链表的长度
  5. s = headA;
  6. while (s) {
  7. lenA ++;
  8. s = s->next;
  9. }
  10. s = headB;
  11. while (s) {
  12. lenB ++;
  13. s = s->next;
  14. }
  15. // 求出两个链表长度差
  16. if (lenA > lenB) {
  17. l = headA, s = headB;
  18. gap = lenA - lenB;
  19. } else {
  20. l = headB, s = headA;
  21. gap = lenB - lenA;
  22. }
  23. // 尾部对齐
  24. while (gap--) l = l->next;
  25. // 移动,并检查是否有相同的元素
  26. while (l) {
  27. if (l == s) return l;
  28. l = l->next, s = s->next;
  29. }
  30. return NULL;
  31. }

面试题02.07.链表相交 - 图11