力扣刷题:单链表OJ篇(中)

大家好,这里是小编的博客频道
小编的博客:就爱学编程

很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!

在这里插入图片描述


废话不多说,我们直接看题。


1.相交链表

(1)题目描述

在这里插入图片描述


(2)解题思路

简单思路如果相交,则必有交点及之后两链表长度一样,各自加上交点之前不同或相同的链表长度,就是各自的链表总长度。所以我们用两个指针分别指向长链表和短链表,先让指向长链表的指针走差距步,再同时走就可以走到相交点(如果存在)。

代码实现:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    
    
    //先得到两条链表的长度
    int lA = 0, lB = 0;
    struct ListNode* failA = headA, * failB = headB;
    while(failA){
    
    
        failA = failA->next;
        ++lA;
    }
    while(failB){
    
    
        failB = failB->next;
        ++lB;
    }
    if(failB != failA) return NULL;
    int gap = lA > lB ? lA-lB : lB-lA;
    //一个小小的技巧找到长的链表并走差距步
    struct ListNode* longlist = headA, * shortlist = headB;
    if(lA < lB){
    
    
        longlist = headB;
        shortlist = headA;
    }
    while(gap--){
    
    
        longlist = longlist->next;
    }
    //依次比较
    while(longlist && shortlist){
    
    
        if(longlist == shortlist){
    
              //该处不可用值相等作为相等条件
            return longlist;
        }
         longlist = longlist->next;
         shortlist = shortlist->next;
    }
    return NULL;
}

巧妙思路只有当链表 headA headB都不为空时,两个链表才可能相交。因此首先判断链表headAheadB 是否为空,如果其中至少有一个链表为空,则两个链表一定不相交,返回 NULL
当链表headA headB 都不为空时,创建两个指针p1 p2,初始时分别指向两个链表的头节点 headA headB,然后将两个指针依次遍历两个链表的每个节点。具体做法如下:

  • 每步操作需要同时更新指针 p1 p2
  • 如果指针 p1 不为空,则将指针 p1 移到下一个节点;如果指针 p2 不为空,则将指针 p2 移到下一个节点。
  • 如果指针 p1 为空,则将指针 p1移到链表 headB 的头节点;如果指针 p2 为空,则将指针 p2 移到链表 headA 的头节点。
  • 当指针 p1p2 指向同一个节点(NULL也包含在内)时,返回它们指向的节点(NULL)。

证明方法:

情况一:两个链表相交

链表headA headB 的长度分别是m n。假设链表 headA 的不相交部分有 a个节点,链表headB的不相交部分有 b 个节点,两个链表相交的部分有c个节点,则有 a+c=mb+c=n
如果 a=b,则两个指针会同时到达两个链表相交的节点,此时返回相交的节点;
如果 a!=b,则指针p1会遍历完链表 headA,指针 p2 会遍历完链表 headB,两个指针不会同时到达链表的尾节点,然后指针 p1移到链表headB的头节点,指针 p2 移到链表 headA 的头节点,然后两个指针继续移动,在指针 p1移动了 a+c+b 次、指针 p2 移动了 b+c+a 次之后,两个指针会同时到达两个链表相交的节点,该节点也是两个指针第一次同时指向的节点,此时返回相交的节点。

情况二:两个链表不相交

链表 headAheadB 的长度分别是 m n。考虑当 m=nm!=n时,两个指针分别会如何移动:
如果 m=n,则两个指针会同时到达两个链表的尾节点,然后同时变成空值 NULL,此时返回 NULL
如果 m!=n,则由于两个链表没有公共节点,两个指针也不会同时到达两个链表的尾节点,因此两个指针都会遍历完两个链表,在指针 p1 移动了 m+n 次、指针p2 移动了 n+m 次之后,两个指针会同时变成空值 NULL,此时返回NULL

代码实现:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    
    
    if (headA == NULL || headB == NULL) {
    
    
        return NULL;
    }
    struct ListNode *p1 = headA, *p2 = headB;
    while (p1 != p2) {
    
    
        p1 = p1 == NULL ? headB : p1->next;
        p2 = p2 == NULL ? headA : p2->next;
    }
    return p1;
}

(3)复杂度分析

说明:此处小编不完全采用大O的渐进表示法,而用相对精准的表示法,以此看出解法二的优势之处。并且以不相交为例

  • 解法一:时间复杂度:2m + 2n ;空间复杂度:O(1)
  • 解法二:时间复杂度:2m + 2n2m(也是2n) ;空间复杂度:O(1)

实际上,小编认为两种解法都各有优势和劣势:

  • 解法一代码较长但是可读性强,思路也好想;
  • 解法二代码简洁运行速度略胜一筹,但思路不好想,可读性也不强。

2.随机链表的复制

(1)题目描述

在这里插入图片描述
在这里插入图片描述

  • 简单来说,就是复制一条带有随机指针的单链表。

(2)解题思路

  • 我们首先将该链表中每一个节点拆分为两个相连的节点,例如对于链表 A→B→C,我们可以将其拆分为 A→A ′ →B→B ′ →C→C ′ 。对于任意一个原节点 S,其拷贝节点 S ′ 即为其后继节点。
  • 这样,我们可以直接找到每一个拷贝节点 S ′的随机指针应当指向的节点,即为其原节点 S 的随机指针指向的节点 T 的后继节点 T ′ 。需要注意原节点的随机指针可能为空,我们需要特别判断这种情况。
  • 当我们完成了拷贝节点的随机指针的赋值,我们只需要将这个链表按照原节点与拷贝节点的种类进行拆分即可,只需要遍历一次。同样需要注意最后一个拷贝节点的后继节点为空,我们需要特别判断这种情况。

代码实现:

struct Node* copyRandomList(struct Node* head) {
    
    
    if (head == NULL)
        return NULL;
    //
    struct Node* cur = head;
    while (cur) {
    
    
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;
        struct Node* next = cur->next;
        cur->next = copy;
        cur = copy->next = next;
        copy->random = NULL;
    }
    //yyds
    cur = head;
    while (cur) {
    
    
        if (cur->random != NULL) 
            cur->next->random = cur->random->next;
        cur = cur->next->next;
    }
    //
    cur = head;
   struct Node dummy = {
    
    };
   struct Node* phead = &dummy;
   while(cur){
    
    
    struct Node* copy = cur->next;
    struct Node* next = copy->next;
    phead = phead->next = copy;
    cur = cur->next = next;
   }
   return dummy.next;
}

(3)复杂度分析

时间复杂度:O(n)

空间复杂度:O(1)。注意返回值不计入空间复杂度。


快乐的时光总是短暂,咱们下篇博文再见啦!!!如果小编的文章会对你有帮助的话不要忘了,记得给小编点点赞和收藏支持一下,在此非常感谢!!!