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

简介: 递归的写法看起来简洁,实际并没有迭代写法好理解,而且在空间复杂度上也比迭代高,这里的递归写法思路主要是先向下找到尾结点后,向上逐个返回,如果等于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;
}


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

相关文章
|
1月前
|
存储 缓存 NoSQL
Redis常见面试题全解析
Redis面试高频考点全解析:从过期删除、内存淘汰策略,到缓存雪崩、击穿、穿透及BigKey问题,深入原理与实战解决方案,助你轻松应对技术挑战,提升系统性能与稳定性。(238字)
|
3月前
|
存储 安全 测试技术
Python面试题精选及解析
本文详解Python面试中的六大道经典问题,涵盖列表与元组区别、深浅拷贝、`__new__`与`__init__`、GIL影响、协程原理及可变与不可变类型,助你提升逻辑思维与问题解决能力,全面备战Python技术面试。
149 0
|
1月前
|
监控 Java 关系型数据库
面试性能测试总被刷?学员真实遇到的高频问题全解析!
面试常被性能测试题难住?其实考的不是工具,而是分析思维。从脚本编写到瓶颈定位,企业更看重系统理解与实战能力。本文拆解高频面试题,揭示背后考察逻辑,并通过真实项目训练,帮你构建性能测试完整知识体系,实现从“会操作”到“能解决问题”的跨越。
|
5月前
|
Web App开发 缓存 前端开发
浏览器常见面试题目及详细答案解析
本文围绕浏览器常见面试题及答案展开,深入解析浏览器组成、内核、渲染机制与缓存等核心知识点。内容涵盖浏览器的主要组成部分(如用户界面、呈现引擎、JavaScript解释器等)、主流浏览器内核及其特点、从输入URL到页面呈现的全过程,以及CSS加载对渲染的影响等。结合实际应用场景,帮助读者全面掌握浏览器工作原理,为前端开发和面试提供扎实的知识储备。
247 4
|
1月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
5月前
|
存储 安全 Java
2025 最新史上最全 Java 面试题独家整理带详细答案及解析
本文从Java基础、面向对象、多线程与并发等方面详细解析常见面试题及答案,并结合实际应用帮助理解。内容涵盖基本数据类型、自动装箱拆箱、String类区别,面向对象三大特性(封装、继承、多态),线程创建与安全问题解决方法,以及集合框架如ArrayList与LinkedList的对比和HashMap工作原理。适合准备面试或深入学习Java的开发者参考。附代码获取链接:[点此下载](https://pan.quark.cn/s/14fcf913bae6)。
3081 48
|
5月前
|
前端开发 JavaScript 开发者
2025 最新 100 道 CSS 面试题及答案解析续篇
本文整理了100道CSS面试题及其答案,涵盖CSS基础与进阶知识。内容包括CSS引入方式、盒模型、选择器优先级等核心知识点,并通过按钮、卡片、导航栏等组件封装实例,讲解单一职责原则、样式隔离、响应式设计等最佳实践。适合前端开发者巩固基础、备战面试或提升组件化开发能力。资源地址:[点击下载](https://pan.quark.cn/s/50438c9ee7c0)。
131 5
2025 最新 100 道 CSS 面试题及答案解析续篇
|
5月前
|
缓存 NoSQL Java
Java Redis 面试题集锦 常见高频面试题目及解析
本文总结了Redis在Java中的核心面试题,包括数据类型操作、单线程高性能原理、键过期策略及分布式锁实现等关键内容。通过Jedis代码示例展示了String、List等数据类型的操作方法,讲解了惰性删除和定期删除相结合的过期策略,并提供了Spring Boot配置Redis过期时间的方案。文章还探讨了缓存穿透、雪崩等问题解决方案,以及基于Redis的分布式锁实现,帮助开发者全面掌握Redis在Java应用中的实践要点。
319 6
|
5月前
|
NoSQL Java 微服务
2025 年最新 Java 面试从基础到微服务实战指南全解析
《Java面试实战指南:高并发与微服务架构解析》 本文针对Java开发者提供2025版面试技术要点,涵盖高并发电商系统设计、微服务架构实现及性能优化方案。核心内容包括:1)基于Spring Cloud和云原生技术的系统架构设计;2)JWT认证、Seata分布式事务等核心模块代码实现;3)数据库查询优化与高并发处理方案,响应时间从500ms优化至80ms;4)微服务调用可靠性保障方案。文章通过实战案例展现Java最新技术栈(Java 17/Spring Boot 3.2)的应用.
450 9
|
5月前
|
缓存 算法 NoSQL
校招 Java 面试高频常见知识点深度解析与实战案例详细分享
《2025校招Java面试核心指南》总结了Java技术栈的最新考点,涵盖基础语法、并发编程和云原生技术三大维度: 现代Java特性:重点解析Java 17密封类、Record类型及响应式Stream API,通过电商案例演示函数式数据处理 并发革命:对比传统线程池与Java 21虚拟线程,详解Reactor模式在秒杀系统中的应用及背压机制 云原生实践:提供Spring Boot容器化部署方案,分析Spring WebFlux响应式编程和Redis Cluster缓存策略。
147 0

推荐镜像

更多
  • DNS