代码随想录训练营day4 | 链表2 | LeetCode 24. 两两交换链表中的节点 、 19.删除链表的倒数第N个节点 、面试题 02.07. 链表相交 、142.环形链表II

24. 两两交换链表中的节点

题目链接:力扣-24. 两两交换链表中的节点

思路:

  • 双指针法

这道题目比较简单,利用双指针法就能解决,一开始写的过于复杂,导致最后超时了,超时原因还没找到,可能写的时候有些我没注意到的地方。但具体思路是一致的,后续对代码进行优化,顺利通过编译。做了个图来辅助理解,建立虚拟头节点会更加方便。

 双指针法代码:

struct ListNode* swapPairs(struct ListNode* head){
    struct ListNode *shead = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode *p, *q;
    shead->next = head;
    p = shead;
    q = shead->next;
    while(p && q && q->next){
        p->next = q->next;
        q->next = p->next->next;
        p->next->next = q;
        p = q;
        q = q->next;
    }
    return shead->next;
}
  • 递归

  1. 先递推到链表尾节点或者空节点。
  2. 回归到上一层返回的newhead(后一组交换节点后的前一个元素),用来与前一组两两节点交换后相链接。
  3. 两节点进行交换。
  4. 继续往上回归,重复34,最后返回的newhead就是两两交换后的新链表的头结点。

递归法理解起来很抽象,知道了思路,写代码时总有一种无从下手的感觉,画了一张图来辅助理解递归的思想。

等我完全学会了递归的思想,会专门出一期博客来总结递归,学习就是一个扫盲的过程。

递归法代码:

struct ListNode* swapPairs(struct ListNode* head){
    //递归结束条件:头节点不存在或头节点的下一个节点不存在
    if(!head || !head->next){
        return head;
    }
    struct ListNode *newhead = head->next;
    head->next = swapPairs(newhead->next);
    newhead->next = head;
    return newhead;
}

19.删除链表的倒数第N个节点 

题目链接:力扣-19. 删除链表的倒数第 N 个结点

思路:

关键在于如何找到删除节点的前一个节点,题目给我们的n是倒数的第n个节点,因此可以利用双指针的方法,让快指针比慢指针多走n步,再让快慢指针同时移动,这样快慢指针始终保持n的距离,当快指针指向末尾(null)时,慢指针刚好走到删除的节点。

注:我们要删除节点需要找到该节点的前一个节点,因此需要快指针比原先多走一步,总共需要先走(n+1)步。


双指针法代码:

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
    struct ListNode *shead = (struct ListNode*)malloc(sizeof(struct ListNode));
    shead->val = 0;
    shead->next = head;
    struct ListNode *fast = shead;
    struct ListNode *slow = shead;
    n++;
    while(n-- && fast){
        fast = fast->next; 
    }
    while(fast){
        slow = slow->next;
        fast = fast->next;
    }
    struct ListNode *p = slow->next;
    slow->next = p->next;
    free(p);
    return shead->next;
}

直接计算链表长度代码:

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
    struct ListNode *shead = (struct ListNode*)malloc(sizeof(struct ListNode));
    shead->val = 0;
    shead->next = head;
    struct ListNode *p = shead;
    struct ListNode *q = shead;
    int length = 0;
    while(p->next){
        p = p->next; 
        length++;
    }
    for(int i = 1; i < length-n+1; i++){
        q = q->next;
    }
    struct ListNode *s = q->next;
    q->next = s->next;
    free(s);
    return shead->next;
}

面试题 02.07. 链表相交

题目链接:力扣-面试题 02.07. 链表相交

思路:

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

先分别求出两条链表的长度,求出差值,将短链表移动到和长链表末尾相对齐的位置。此时就能比较其节点是否相同,若不相同,则将指针都往后移一位,继续比较。

这里需要注意链表都为空的情况,在定义指针时都赋值NULL。

代码:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode *l = NULL, *s = NULL;
    int lengthA = 0, lengthB = 0, gap = 0;
    s = headA;
    while(s->next){
        s = s->next;
        lengthA++;
    }
    s = headB;
    while(s->next){
        s = s->next;
        lengthB++;
    }
    if(lengthA > lengthB){
        l = headA;
        s = headB;
        gap = lengthA - lengthB;
    }
    else{
        l = headB;
        s = headA;
        gap = lengthB - lengthA;
    }
    while(gap--){
        l = l->next;
    }
    while(l){
        if(l == s){
            return l;
        }
        l = l->next;
        s = s->next;
    }  
    return NULL;
}

看题解还有一种双指针法,还没完全理解,等想明白了再来写思路,链接先贴这,供大家一起学习。https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/solutions/1395092/lian-biao-xiang-jiao-by-leetcode-solutio-2kne/ 

142.环形链表II

题目链接:力扣-142. 环形链表 II

思路:

难度有点大,看完题解后,果然编程的本质是数学。

还未能完全掌握,二刷的时候需要注意。

解题的关键在于:链表是否有环、有环时如何找到该环的入口。

我们使用两个指针,fast 与 slow,让它们都指向链表的头节点。让slow 每移动一个位置,fast 移动两个位置。如果链表中存在环,那它们必然在环中相遇。

设链表中环外部分的长度为 x,环形入口节点到 fast指针与slow指针相遇节点的长度为y,从相遇节点再到环形入口节点长度为 z。

 

当两指针相遇时, slow指针走过的长度为: x + y, fast指针走过的长度为:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针。

 由fast指针走过的长度 = slow指针走过的长度 * 2可得:

(x + y) * 2 = x + y + n (y + z)

x = n (y + z) - y 

x = (n - 1) (y + z) + z

因为 fast指针至少要多走一圈才能与slow指针相遇,因此n一定大于等于1。

当 n=1时, x = z。

定义指针 f 指向相遇节点,指针 h 指向头结点处。

让 f 和 h 同时移动,相遇的地方就是环形入口的节点。

贴上学习的文章:代码随想录

代码:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *fast = head, *slow = head;
    while(fast && fast->next){
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow){
            struct ListNode *f = fast, *h = head;
            while(f != h){
                f = f->next;
                h = h->next;
            }
            return f;
        }
    }
    return NULL;
    
}

总结:

今天除了环形链表,其他的题目大部分都有思路。

链表中双指针法与递归法很重要,基本上每道题都能用这两种方法解决,后期需要多巩固。

这两天的练习中,慢慢的开始能动的了手了,也算是一种进步,但是速度比较慢,花费的时间很多,新专题的练习需要更加注重效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值