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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 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;
}


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

相关文章
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
93 2
|
1月前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
513 6
|
1月前
|
Java 程序员
面试官的加分题:super关键字全解析,轻松应对!
小米,29岁程序员,通过一个关于Animal和Dog类的故事,详细解析了Java中super关键字的多种用法,包括调用父类构造方法、访问父类成员变量及调用父类方法,帮助读者更好地理解和应用super,应对面试挑战。
42 3
|
2月前
|
存储 NoSQL MongoDB
MongoDB面试专题33道解析
大家好,我是 V 哥。今天为大家整理了 MongoDB 面试题,涵盖 NoSQL 数据库基础、MongoDB 的核心概念、集群与分片、备份恢复、性能优化等内容。这些题目和解答不仅适合面试准备,也是日常工作中深入理解 MongoDB 的宝贵资料。希望对大家有所帮助!
|
2月前
|
缓存 前端开发 JavaScript
"面试通关秘籍:深度解析浏览器面试必考问题,从重绘回流到事件委托,让你一举拿下前端 Offer!"
【10月更文挑战第23天】在前端开发面试中,浏览器相关知识是必考内容。本文总结了四个常见问题:浏览器渲染机制、重绘与回流、性能优化及事件委托。通过具体示例和对比分析,帮助求职者更好地理解和准备面试。掌握这些知识点,有助于提升面试表现和实际工作能力。
70 1
|
3月前
|
程序员 C语言
【C语言】LeetCode(力扣)上经典题目
【C语言】LeetCode(力扣)上经典题目
|
3月前
|
索引
力扣(LeetCode)数据结构练习题(3)------链表
力扣(LeetCode)数据结构练习题(3)------链表
107 0
|
3月前
力扣(LeetCode)数据结构练习题(2)
力扣(LeetCode)数据结构练习题(2)
35 0
|
15天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
15天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析

推荐镜像

更多