第四天打卡(主要思考环型链表)

为了方便题目不再赘述,直接发链接,依旧C语言

一、两两交换链表中的节点

1、力扣题目

24. 两两交换链表中的节点 - 力扣(LeetCode)

2、初次写题

使用循环遍历,两两交换

代码如下:

struct ListNode* swapPairs(struct ListNode* head) {
    typedef struct ListNode ListNode;
    // //设一个虚拟头节点
    struct ListNode *shead=(ListNode*)malloc(sizeof(ListNode));
    shead->next=head;

    struct ListNode *cur=head;
    struct ListNode *pre=shead;
    while(cur!=NULL&&cur->next!=NULL){//条件不能够写反,先判断第一个再判断第二个
        //临时指针tmp
        struct ListNode *tmp=cur->next;

        //两两交换
        cur->next=tmp->next;
        pre->next=tmp;
        tmp->next=cur;

        //更新cur和pre
        pre=cur;
        cur=cur->next;
    }
    return shead->next; 
}

还可以写成递归,代码如下:

struct ListNode* swap(struct ListNode* pre, struct ListNode* cur,struct ListNode* shead){
        //返回
        if(cur==NULL||cur->next==NULL)return shead->next;
        //临时指针tmp
        struct ListNode *tmp=cur->next;
        //两两交换
        cur->next=tmp->next;
        pre->next=tmp;
        tmp->next=cur;
        //递归调用
        return swap(cur,cur->next,shead);
}
struct ListNode* swapPairs(struct ListNode* head) {
    typedef struct ListNode ListNode;
    // //设一个虚拟头节点
    ListNode *shead=(ListNode*)malloc(sizeof(ListNode));
    shead->next=head;
    return swap(shead,head,shead);
}

3、视频学习

视频思路跟上述想法差不多就不再放了。

4、总结

该题需要注意的是在进行交换的时候最好能够手动画一下图,以防止混淆。

二、删除链表倒数第N个节点

1、力扣题目

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

2、初次解题

想法是先循环找有一共有多少个节点,然后在循环找倒数第n个节点,进行删除。

struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
    typedef struct ListNode ListNode;
    //虚拟头节点
    ListNode *shead=(ListNode *)malloc(sizeof(ListNode));
    shead->next=head;

    ListNode *cur=shead;
    //统计节点数
    int i=0;
    while(cur->next!=NULL){
        cur=cur->next;
        i++;
    }
    //cur指向倒数第n+1个节点
    cur=shead;
    while(i-n!=0){
        cur=cur->next;
        i--;
    }
    //释放倒数第n个节点
    ListNode *tmp=cur->next;
    cur->next=tmp->next;
    free(tmp);
    return shead->next;
}

3、视频学习

视频地址:链表遍历学清楚! | LeetCode:19.删除链表倒数第N个节点_哔哩哔哩_bilibili

思路解析:

(1)首先设定一个虚拟头节点和快慢指针。

(2)该题可抽象为移动一个固定长度(长度为n)的窗口,当窗口右边指向null时,slow指向的即使倒数第n个节点。

(3)不过需注意,释放节点时需要知道前后节点,所以初始时让fast为head,此时slow的next就是需要释放的节点。

代码如下:

struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
    typedef struct ListNode ListNode;
    //虚拟头节点
    ListNode *shead=(ListNode *)malloc(sizeof(ListNode));
    shead->next=head;
    //快慢指针
    ListNode *slow=shead;
    ListNode *fast=shead->next;
    //滑动窗口,长度为n
    while(n--){
        fast=fast->next;
    }
    while(fast!=NULL){
        fast=fast->next;
        slow=slow->next;
    }
    //释放倒数第n个节点
    ListNode *tmp=slow->next;
    slow->next=tmp->next;
    free(tmp);
    return shead->next;
}

三、链表相交

1、力扣题目

面试题 02.07. 链表相交 - 力扣(LeetCode)

2、写题和学习

思路跟我初次思路一致,只不过后面再次从头开始的时候我用了cur1代表长链表head,cur2代表短链表head,多花了无用的空间,下次可以直接重用。

直接上代码:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    typedef struct ListNode ListNode;
    ListNode *a=headA;
    ListNode *b=headB;
    int size_a=0,size_b=0,n;//n为链表之间的大小差

    //查看最后一个节点,并且统计节点数(如果有节点的话比实际数小1)计算n不影响
    while(a!=NULL&&a->next!=NULL){
        a=a->next;
        size_a++;
    }
    while(b!=NULL&&b->next!=NULL){
        b=b->next;
        size_b++;
    }
    //最后一个节点数不同,则不相交
    if(a!=b)return NULL;
    //计算n,此时a为长,b为短
    if(size_a>=size_b){
        a=headA;b=headB;
        n=size_a-size_b;
    }else{
        a=headB;b=headA;
        n=size_b-size_a;
    }
    //移动长链表head,使得距离交汇节点距离相同。
    while(n--){
        a=a->next;
    }
    //找交汇节点
    while(a!=b){
        a=a->next;
        b=b->next;
    }
    return a;
}

这里提一下官方解析中的双指针法,该解法太巧妙了,该解法的思路就是,让两个指针走m+n的长度,最后一定会重合。如图所示,假如公共段为黑色,则按上述走法,一定会出现a=b的情况,如果没用公共段,那么最后就会一起走到null,此时a=b正好返回null。

四、环形链表

1、力扣题目

142. 环形链表 II - 力扣(LeetCode)

2、视频学习

视频地址:

把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II_哔哩哔哩_bilibili

思路解析:相当于一个横向有助跑道(长为x)的操场,从助跑道开始,一个跑的快,一个跑的慢,如果操场是环型跑道,那么快的一定会跟慢的重合,否则不是环型跑道。

当重合的时候,快的人已经跑了:x+n圈+距环入口正向距离y,慢的跑了:x+(n-m)圈+距环入口正向距离y,跑完一圈还剩z距离。此时的x=z(看视频的教学自己手动推导一下)所以只需要,其中一个指针从头开始,一个指针从重合处,同时以相同速度前进,重逢的时候就是环入口处。

struct ListNode *detectCycle(struct ListNode *head) {
    typedef struct ListNode ListNode;
    //虚拟头节点
    ListNode *shead=(ListNode *)malloc(sizeof(ListNode));
    shead->next=head;
    //快慢指针
    ListNode *fast=shead;
    ListNode *slow=shead;
    while(fast->next!=NULL&&fast->next->next!=NULL){//是否循环
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow){//循环则找入口
            slow=shead;//从头和重合处,同时以相同速度前进
            while(fast!=slow){
                slow=slow->next;
                fast=fast->next;
            }
            return fast;
        }
    }
    return NULL;
}

3、总结

该题的思路十分值得学习,应该多看一下。注意重合时推导出来x=z(可以假设fast每次移动2,slow移动1,根据重合时fast走的路程为2倍的slow的路程进行推导)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值