单链表leetcode刷题/上(C语言版)

目录

注意:由于翻译原因,在leetcode链表题中,所有的“头结点”指的都是首元结点,而非真正意义上的首元结点

题目1:移除链表元素

题目2:反转链表

题目3:链表的中间结点


 

注意:由于翻译原因,在leetcode链表题中,所有的“头结点”指的都是首元结点,而非真正意义上的首元结点

 

题目1:移除链表元素

6701a55a87b647a6b9abf5973d21994d.png

解题思路:

        思路1:定义一个prev指针,指向需要删除的结点的前一个结点;一个next指针,指向需要删除的结点的后一个结点。最后将判断是否当前结点需要删除的指针叫做pcur。如果pcur当前所指向的并不是需要删除的位置,那么prev以及next同时往后走;反之,则先让prev指向next(为了防止pcur找不到),然后对pcur使用free操作,再让pcur往后走。知道pcur指向空。具体思路如下图所示。

        思路2:创建一个新链表newHead,找值不为val的结点,尾插到新链表中。新链表需要有一个指向头的指针,来实现最终的返回;以及一个指向尾的指针,来实现尾插操作。遍历到pcur指向空。请注意,此处所谓的创建新链表只是在逻辑上而言的,实际在内存中只是创建了三个指针变量(pcur、newHead、newTail),而并没有通过malloc动态开辟一个新的链表空间;让newHead指向第一个满足条件的结点,让newTail不断向后查找满足条件的结点。最后也只是指针对于已经开辟的空间进行了一系列的操作,最终返回结果。具体思路如下图所示。

 思路1流程图:

4f3ae087787848dfaa304da46938a0be.jpg

a23024fb381f41f698831ba950c4d7dc.jpg 

 

思路2流程图:

641e6e772dab4d4093465d5d258bddb7.jpg

 b3504ffa7b1a493ebdb8c2f88119163b.jpg

 

代码:

 typedef struct ListNode listnode;
struct ListNode* removeElements(struct ListNode* head, int val) {
    listnode* n1,*n2;//定义两个指针,一个指向原链表,一个指向新链表
    listnode* newhead;//新链表的首元结点
    newhead=n2=NULL;//新链表初始情况为空,因此首元节点和指向新链表的指针均为空
    n1=head;//n1指向原链表的首元节点
    if(head==NULL)//原链表为空的情况
    {
        return head;
    }
    else //非空的情况
    {
        while(n1)//循环退出条件:原链表遍历完成
        {
            if(n1->val!=val)//判断是否不为val值,不是val值的全都放到新链表中
            {
                if(newhead==NULL)//新链表为空的情况
                {
                    newhead=n2=n1; //创建
                }
                else //非空的情况
                {
                    n2->next=n1;//尾插
                    n2=n2->next;
                }
            }
        n1=n1->next;
        }
        if(n2)//遍历完以后,新链表中有值,为防止情况3,提示语句
        {
            n2->next=NULL; //为了防止情况1,为val值的结点在原链表最后一位,因此n2的next还是指向了原链表的最后一位
        }
        return newhead;
        }
}

题设是struct ListNode类型,笔者通过typedef改成了listnode。

最后的一个if语句(即代码注释所标注的提示语句),先是为了防止leetcode题设示例1中,6是最后一个结点,直接代替 5 所在结点成为尾结点。如下图所示。

而if语句的进入条件是为了防止示例3,这种情况下,将会对空指针解引用。

10ad39a3b2f94d5e8d1da9156e3272df.jpg

52944546a3fb4663a25d1d362c4c4c1f.png

 

题目2:反转链表

b49f9c9249ab4322aede66e093a07776.png

解题思路:

        思路1:创一个新链表,原链表从头向后遍历,新链表从后往前头插。

        思路2:创建三个指针,分别称为n1、n2、n3。初始情况下,n1指向为空,n2指向首元结点,n3指向首元结点的下一个结点。让n2指向n1,然后n1、n2、n3一起往后走1位,重复该操作,直到n2指向为空,具体流程如下图所示。

475887e9e99c4c67b99d6b57f5f6ac46.jpg
faae280e961546869f4b33284498ca6a.jpg

709ac41693cf43729e26602c78999656.jpg 

代码:

 typedef struct ListNode listnode;
struct ListNode* reverseList(struct ListNode* head) {
    if(head==NULL)
    {
        return NULL; //链表为空
    }
    else{
    listnode* n1,*n2,*n3;
    n1=NULL;
    n2=head;
    n3=head->next;
    while(n2)
    {
        n2->next=n1;
        n1=n2;//n1、n2同时往后走
        n2=n3;
        if(n3) //提示语句
        {
            n3=n3->next; //n3往后走
        }
    }
    return n1;//循环结束时,n1恰好指向结果链表的首元结点
    }
    }

代码中往后走的语句出现了“n1=n2”,这会不会导致n2结点指向自己?

答案是不会。n1=n2是让n1的值从NULL变成n2指向的位置,如果要让n2指向自己,需要是n2=n1才行,即n2的下一个结点为n1,n1为n2本身。        

使用if语句(即代码注释所标注的提示语句)来限制条件的原因:n2指向尾元结点时,n3已经指向为空;当n2向后移动一位以后,还是经过n3向后移动的操作,这就造成了对空指针解引用。

 

f44b5480b4b448b4a78551a4c8d58fe4.png

 

 

题目3:链表的中间结点

ee6a2d2bb8a84b16894cf41a8f7b0904.png

解题思路:

        思路1:遍历原链表,定义一个计数器count。遍历结束以后,直接返回(count/2)结点的next结点。

        思路2:使用快慢指针的思路。定义两个指针,一个叫做slow一个叫做fast,初始情况下全都指向链表的首元结点。当链表有奇数个结点时,fast指向尾元结点,slow指向所求结点;当链表有偶数个结点时,fast指向空,slow指向所求结点。

132f9c9b1c9b45488649a6b191acd74e.jpg

 a21342d45faa43298a98527e6a71314e.jpg

代码:

 typedef struct ListNode listnode;
struct ListNode* middleNode(struct ListNode* head) {
    //快慢指针
    listnode* slow,*fast;
    //slow每次走一步,fast每次走两步
    slow=fast=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    return slow;
}

while(即代码注释所标注的提示语句)语句的两个限制条件,是否可以更改先后次序?

答案是不可以。前一个fast是为了解决偶数个结点的情况,后一个fast->next是为了解决奇数个结点的情况。当把fast->next放在前,又恰好链表是偶数个结点,那就会出现对空指针解引用的情况。所以两者的先后顺序不能改。

cee05f37270d4b248294fe876bdd6221.png

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值