单链表的几道算法题(1)


在数据结构的学习中,不仅要理解底层代码实现,还要多做算法题来巩固。
下面我给大家列出几道用单链表解决的算法题。
如果对单链表的知识有疑问的话,我上次博客就是对单链表的讲解。欢迎大家收看 OvO

1.移除链表元素

https://leetcode.cn/problems/remove-linked-list-elements/description/%01
在这里插入图片描述
这道题目的是删除链表中值为val的值,并返回头节点。
其实思路很简单,删除每一个值为val的结点即可。但是实现过程有一些需要注意的地方。
1.当我们在遍历过程中遇到值为val的结点,就需要将上一个结点的next指针指向下一个结点。下一个结点好表示,但如何获取上一个结点呢?
我们可以在遍历之前就创建两个结点(prev和pcur),都指向头结点,但是在每次循环的最后先让prev=pcur,再让pcur=pcur->next。
这样就实现了获取前一个结点的目的。

struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode*pcur=head;
    struct ListNode*prev=head;
    while(pcur)
    {

        if(pcur->val==val)
        {
            prev->next=pcur->next;
            pcur=pcur->next;
            continue;
        }
        prev=pcur;
        pcur=pcur->next;
    }

    return head;
}

当我们要删除某一个结点时,prev指针的next指针已经指向了pcur的下一个节点,为了保持prev和pcur始终相差一个结点的距离,我们需要将pcur指向下一个结点,并直接进行下一次循环。
我们提交代码,发现这个样例不通过
在这里插入图片描述
因为这串代码不能将头结点进行处理,所以我们可以这样写

struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode*pcur=head;
    struct ListNode*prev=head;
    while(pcur)
    {

        if(pcur->val==val)
        {
            prev->next=pcur->next;
            pcur=pcur->next;
            continue;
        }
        prev=pcur;
        pcur=pcur->next;
    }
    if(head!=NULL&&head->val==val)
    {
        return head->next;
    }
    return head;
}

这样子我们就完美解决了这道题。

2.反转链表

https://leetcode.cn/problems/middle-of-the-linked-list/description/

在这里插入图片描述
顾名思义,这道题让我们将链表反转。并返回头结点。
我们一定会遍历一遍原链表,那么我们可以先创建一个新链表,并在遍历原链表每一个结点的时候头插到新链表中,那么这道题就解决了。

struct ListNode*NewNode(int x)
 {
    struct ListNode*newnode=(struct ListNode*)malloc(sizeof(struct ListNode));
    if(newnode==NULL)
    {
        perror("malloc fail");
        exit(1);
    }
    newnode->val=x;
    newnode->next=NULL;
    return newnode;
 }
 //链表的头插
 void SLTPushFront(struct ListNode**pphead,int x)
 {
    struct ListNode* newnode1=NewNode(x);
    if(*pphead==NULL)
    {
        (*pphead)=newnode1;
        return;
    }
    newnode1->next=(*pphead);
    (*pphead)=newnode1;
    return;

 }
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode*pcur=head;
    if(head==NULL)
    {
        return NULL;
    }
    struct ListNode*prev=NewNode(pcur->val);
    pcur=pcur->next;
    while(pcur)
    {
        int data=pcur->val;
        SLTPushFront(&prev,data);
        pcur=pcur->next;
    }
    return prev;
}

3.链表的中间结点

https://leetcode.cn/problems/middle-of-the-linked-list/description/

在这里插入图片描述
思路一:
遍历两遍链表,第一遍得出结点个数,算出中间结点位置,当第二遍遍历到中间位置时返回。
但是这种方法我们要遍历两遍,虽然时间复杂度也不高,但终归不是最简方法。
思路二:
快慢指针法,创建一快一慢指针,快指针每次走两步,慢指针每次走一步,当快指针走完整个链表时,慢指针所指的就是中间结点。

推导:1.设链表的长度为2n,那么快指针走完时慢指针走了n步,对应的就是中间位置。
2.设链表的长度为2n+1,那么当快指针走2n步后没走完,再走一次,即走了(2n+2)步,对应慢指针走了n+1步,也成立。

struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode*fast=head;
    struct ListNode*slow=head;
    while(fast)
    {
        if(fast->next==NULL)
        {
            return slow;
        }
        fast=fast->next->next;
        slow=slow->next;
    }
    return slow;
}


如发现错误,欢迎打在评论区。
主页还有更多优质内容OvO

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值