(C语言版)力扣(LeetCode)+牛客网(nowcoder)链表相关面试题OJ题解析(上)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 递归的写法看起来简洁,实际并没有迭代写法好理解,而且在空间复杂度上也比迭代高,这里的递归写法思路主要是先向下找到尾结点后,向上逐个返回,如果等于val值,就将该节点上一个元素直接指向该节点下一个元素,等于是将该点从链表中删除了

5a9e9ab2ae0e45b3b81ed2eac2d9fa3d.png


203. 移除链表元素


题目


给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

题目链接:移除链表元素


解法一:递归


代码如下:


struct ListNode* removeElements(struct ListNode* head, int val){
    if(head==NULL)
        return NULL;
    head->next=removeElements(head->next,val);
    return head->val==val?head->next:head;
}


递归的写法看起来简洁,实际并没有迭代写法好理解,而且在空间复杂度上也比迭代高,这里的递归写法思路主要是先向下找到尾结点后,向上逐个返回,如果等于val值,就将该节点上一个元素直接指向该节点下一个元素,等于是将该点从链表中删除了

如下图:

3e6f948498a440439162e2f05d3f72b5.png


解法二:迭代


代码如下:


struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* headhead=malloc(sizeof(struct ListNode));
    headhead->next=head;
    struct ListNode* temp=headhead;
    while(temp->next!=NULL)
    {
        if(temp->next->val==val)
            temp->next=temp->next->next;
        else
            temp=temp->next;
    }
    return headhead->next;
}


迭代的思路就比较好理解了,先创建一个结点空间headhead,让它指向head,再用temp复制该内存地址,使用temp对链表进行遍历,找到等于val值的元素就删除,直至遍历完整个链表,最后headhead指向的下一个位置即为删除val结点的head头结点位置。


206. 反转链表


题目


给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

题目链接:反转链表


解法一:递归


代码如下:


struct ListNode* reverseList(struct ListNode* head) {
    if (head == NULL || head->next == NULL) {
        return head;
    }
    struct ListNode* newHead = reverseList(head->next);
    head->next->next = head;
    head->next = NULL;
    return newHead;
}


这里的递归写法相对于上一题更不好理解一些,具体也是向下找到最后一个结点,逐个反转,如下图:


0b4cda31fbce4752ab196cc809b47f14.png


解法二:迭代


代码如下:


struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* prev=NULL;
    struct ListNode* cur=head;
    while(cur)
    {
        struct ListNode* next=cur->next;
        cur->next=prev;
        prev=cur;
        cur=next;
    }
    return prev;
}


迭代写法就很简单了,设定两个指针,prev指向NULL,cur指向头结点,再用循环从头开始反转,最后prev即为头结点,返回prev即可。


876. 链表的中间结点


题目


给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

题目链接:链表的中间结点


解法一:快慢指针法


代码如下:


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


个人觉得快慢指针的写法更简洁且好理解,大概的思路就是slow指针走一步,fast指针走两步,fast遍历完链表,slow指针指向的即为中间结点。


解法二:单指针法


代码如下:


struct ListNode* middleNode(struct ListNode* head){
    int n=0;
    struct ListNode* cur=head;
    while(cur!=NULL)
    {
        n++;
        cur=cur->next;
    }
    int k=n/2;
    cur=head;
    while(k>0)
    {
        cur=cur->next;
        k--;
    }
    return cur;
}


这个算法的思路就是用cur指针先遍历一遍数组,用n记录结点个数,再用结点数/2的值赋给k,cur重新指向头结点,再进行遍历k个位置,最后cur停在的结点位置即为中间节点位置。


链表中倒数第k个结点


题目


输入一个链表,输出该链表中倒数第k个结点。

题目链接:链表中倒数第k个结点


解法


代码如下:


struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode* fast=pListHead;
    struct ListNode* slow=pListHead;
    while(k--)
    {
        if(fast==NULL)
        {
            return NULL;
        }
        fast=fast->next;
    }
    while(fast)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return slow;
}


这里用的是双指针的写法,先让fast指针向前走k个结点位置,再让fast和slow同时走,直到fast走到NULL,此时的slow指向的就是倒数第k个结点。


21. 合并两个有序链表


题目


将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

题目链接:合并两个有序链表


解法一:递归


代码如下:


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1==NULL)
        return list2;
    else if(list2==NULL)
        return list1;
    else if(list1->val<list2->val)
    {
        list1->next=mergeTwoLists(list1->next,list2);
        return list1;
    }
    else
    {
        list2->next=mergeTwoLists(list1,list2->next);
        return list2;
    }
}


这种递归写法相对好理解一些,前两个条件是考虑list1或list2为空的情况发生,后两个条件都是使用递归,当list1头结点值小于list2头结点值时,则头结点为list1,否则为list2,然后继续向下一结点递归,最后合并完整个链表。


解法二:迭代


代码如下:


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* list3=(struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* p3=list3;
    while(list1!=NULL&&list2!=NULL)
    {
        if(list1->val<list2->val)
        {
            p3->next=list1;
            list1=list1->next;
            p3=p3->next;
            p3->next=NULL;
        }
        else
        {
            p3->next=list2;
            list2=list2->next;
            p3=p3->next;
            p3->next=NULL;
        }
    }
    if(list1==NULL)
        p3->next=list2;
    if(list2==NULL)
        p3->next=list1;
    return list3->next;
}


这种写法的思路是借助额外的空间,也是逐个比较大小,再将结点从小到大串联,最后返回额外的空间结点指向的下一结点,即为调整好的链表。

相关文章
|
4月前
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
55 1
|
2月前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
873 6
|
4月前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
49 7
|
4月前
|
算法
【链表】算法题(二) ----- 力扣/牛客
【链表】算法题(二) ----- 力扣/牛客
|
4月前
|
存储
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)(一)
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)
|
4月前
|
索引
力扣(LeetCode)数据结构练习题(3)------链表
力扣(LeetCode)数据结构练习题(3)------链表
125 0
|
4月前
|
算法
【链表】算法题(一) ----- 力扣 / 牛客
【链表】算法题(一) ----- 力扣 / 牛客
|
4月前
|
存储 缓存
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)(二)
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)
|
8月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
|
8月前
|
存储 SQL 算法
LeetCode 题目 86:分隔链表
LeetCode 题目 86:分隔链表

推荐镜像

更多