当前位置: 首页 > news >正文

链表OJ习题(1)

一. leetcode 203 移除链表元素

迭代法

https://leetcode.cn/problems/remove-linked-list-elements/description/

typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {ListNode* pcur=head;ListNode* newHead,*newTail;newHead=newTail=NULL;while(pcur){if(pcur->val!=val){if(newHead==NULL){newHead=newTail=pcur;}else{newTail->next=pcur;newTail=newTail->next;}}pcur=pcur->next;} if(newHead)newTail->next=NULL;return newHead;
}

思路

  • 遍历原链表,筛选出不等于目标值的节点
  • 将筛选出的节点依次连接,形成新链表
  • 最后为新链表的尾节点next指针置空,避免链表循环

    解题过程

    • 定义newHead和newTail指针,分别指向新链表的头和尾
      • pcur找到val不为指定的val值时,有两种情况:
        • 1)新链表为空时,要将newHead和newTail都指向当前节点
        • 2)新链表非空时,newHead作为新链表的头结点,无需做任何处理,将当前节点尾插到新链表末尾,newTail指向链表新的尾节点
          • pcur跳出循环有两种情况
            • 1)初始链表为空链表,这时可以直接返回newHead
            • 2)初始链表不是空链表,pcur一直遍历到为空,要记得将newTail的next指针置为空,防止连接到后面的节点

              复杂度

              • 时间复杂度: O(n)
              • 空间复杂度: O(1)

              二. leetcode 206 反转链表

              https://leetcode.cn/problems/reverse-linked-list/description/

              法一:头插法

              typedef struct ListNode ListNode;
              struct ListNode* reverseList(struct ListNode* head) {ListNode* pcur=head;ListNode* newHead,*headNext;newHead=headNext=NULL;while(pcur){ListNode* next=pcur->next;if(newHead==NULL){newHead=headNext=pcur;headNext->next=NULL;}else{//头插newHead=pcur;newHead->next=headNext;headNext=newHead;}pcur=next;}return newHead;
              }

              思路

              • 用headNext记录新的头结点newHead的下一个节点
              • 每次循环先保存下一个节点,然后将原链表的节点头插到新链表中
              • 移动指针继续遍历,最终newHead成为新表头

              法二:迭代法

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

              思路

              • 用prev记录前节点
              • 每次循环先保存下一个节点,再将当前节点的next指针指向prev节点
              • 移动指针继续遍历,最终prev成为新表头

                解题过程

                • 先创建两个指针,pcur和prev
                • pcur用来遍历原链表,prev用来保存pcur节点的前一个节点
                • 在遍历时,再创建next指针用来存储pcur节点的下一个节点,将pcur的next指针指向改变后,pcur继续遍历,跳出循环后prev即为新链表的头节点

                  复杂度

                  • 时间复杂度: O(n)
                  • 空间复杂度: O(1)

                  三. leetcode 876 链表的中间节点

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

                  法一:计数法

                  typedef struct ListNode ListNode;
                  struct ListNode* middleNode(struct ListNode* head) {//节点个数ListNode*pcur=head;int count=0;while(pcur){count++;pcur=pcur->next;}int mid=count/2;ListNode* midNode=head;while(mid--){midNode=midNode->next;}return midNode;
                  }

                  思路

                  • 第一次遍历链表记录节点总个数
                  • 第二次遍历到中间节点返回该节点

                    复杂度

                    • 时间复杂度: O(n)
                    • 空间复杂度: O(1) 

                    法二:快慢指针法

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

                    思路

                    • 慢指针每次走一步,快指针每次走两步
                    • 快指针为空或快指针的下一个指针为空时,slow指针指向的节点即为中间节点
                    • 注意:循环条件中 fast!=NULL 必须放在前面。

                      复杂度

                      • 时间复杂度: O(n) 
                      • 空间复杂度: O(1) 

                        四. leetcode 21 合并两个有序链表

                        https://leetcode.cn/problems/merge-two-sorted-lists/description/

                        typedef struct ListNode ListNode;
                        struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {ListNode*l1,*l2,*l3;l1=list1;l2=list2;ListNode*newHead,*newTail;newHead=newTail=(ListNode*)malloc(sizeof(ListNode));//后面会用到newTail的next指针,所以需要初始化newTail->next=NULL;//l1用来遍历List1,l2用来遍历List2while(l1!=NULL && l2!=NULL){if(l1->val<l2->val){newTail->next=l1;newTail=newTail->next;l1=l1->next;}else{newTail->next=l2;newTail=newTail->next;l2=l2->next;}}//遍历完l2跳出循环if(l1){//将newTail的next指针指向l1可直接将l1后面的节点连接起来newTail->next=l1;}//遍历完l1跳出循环if(l2){//将newTail的next指针指向l2可直接将l2后面的节点连接起来newTail->next=l2;}ListNode* retNode=newHead->next;//申请完空间要释放free(newHead);newHead=NULL;return retNode;
                        }

                        思路

                        • 创建虚拟头结点
                        • 创建两个指针分别遍历两个链表,每次取较小的节点接入新链表
                        • 拼接剩余未遍历完的链表
                        • 释放虚拟节点,返回新的链表头节点

                          复杂度

                          • 时间复杂度: O(n)
                          • 空间复杂度: O(1)

                              五. 牛客网 链表分割

                              https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70

                              class Partition {
                              public:ListNode* partition(ListNode* pHead, int x) {ListNode* lessHead,*lessTail;lessHead=lessTail=(ListNode*)malloc(sizeof(ListNode));ListNode* greaterHead,* greaterTail;greaterHead=greaterTail=(ListNode*)malloc(sizeof(ListNode));ListNode* pcur=pHead;while(pcur){if(pcur->val<x){lessTail->next=pcur;lessTail=lessTail->next;}else{greaterTail->next=pcur;greaterTail=greaterTail->next;}pcur=pcur->next;}greaterTail->next=NULL;lessTail->next=greaterHead->next;ListNode* ret=lessHead->next;free(lessHead);lessHead=NULL;return ret;}
                              };

                              思路

                              • 定义两个哨兵节点(lessHead和greaterHead),分别作为两个临时链表的头节点
                              • 遍历原链表,根究每个节点值的大小,将其插入到对应的链表的尾部
                              • 遍历结束后,要将保存大于x值节点的尾节点的next指针指向NULL,防止形成环
                              • 最后将保存小于x的链表的尾节点,与保存大于x值节点的头结点连接

                              复杂度

                              • 时间复杂度: O(n)
                              • 空间复杂度: O(1)

                              总结

                              本篇博客深入解析了经典的链表OJ习题,旨在帮助读者掌握链表操作的核心技巧与解题思路,如快慢指针、双指针等。通过对典型例题的剖析,助你巩固数据结构基础。

                              如果大家在练习中发现新的解题角度,或是有没搞懂的知识点,欢迎在评论区留言讨论。后续我也会持续更新数据结构相关的 OJ 解析,咱们一起在刷题中夯实基础,稳步进阶!​

                              http://www.dtcms.com/a/349619.html

                              相关文章:

                            • redis-缓存-持久化
                            • 使用 Gemini CLI作为 Claude Code的 subagent
                            • OC-MVC模式下的折叠cell
                            • 利用 Python 爬虫获取 1688 商品详情 API 返回值说明(代码示例)实战指南
                            • 爬虫基础学习-爬取网页项目
                            • vue2使用WaveSurfer实现简易的音频播放
                            • 波音787项目:AR技术重塑航空制造的数字化转型
                            • 用MessageBus优化模块通信:实现订阅/发布模式
                            • nmcli命令详解
                            • 文吃透朴素贝叶斯:从原理到实战
                            • 【python文件处理】使用 open() 函数打开文件、 File 操作文件、使用 OS 对象操作文件目录的知识,使用 open() 函数打开文件
                            • DMP-Net:面向脑组织术中成像的深度语义先验压缩光谱重建方法|文献速递-深度学习人工智能医疗图像
                            • Android进入Activity时闪黑生命周期销毁并重建
                            • 集成电路学习:什么是Caffe深度学习框架
                            • 强化学习核心概念与算法详解-马尔可夫决策过程(MDP)+贝尔曼方程(Bellman Equation)
                            • 合同管理软件的主要功能有什么?
                            • 朴素贝叶斯学习笔记:从原理到实战(J享)
                            • (LeetCode 每日一题) 498. 对角线遍历 (矩阵、模拟)
                            • SSM从入门到实战:3.2 SpringMVC请求处理与控制器
                            • 《C++哈希表:高效数据存储与检索的核心技术》
                            • 朴素贝叶斯算法学习总结
                            • MySQL 磁盘和 Redis 内存
                            • 无人机航拍数据集|第22期 无人机城市交通目标检测YOLO数据集8624张yolov11/yolov8/yolov5可训练
                            • Coze用户账号设置修改用户头像-前端源码
                            • 【ACP】2025-最新-疑难题解析-5
                            • Python Day 33 JavaScript BOM 与 DOM 核心笔记整合
                            • 【数学建模】如何总结数学建模中的层次分析法最好
                            • 通过Fiddler肆意修改接口返回数据进行测试
                            • EXCEL自动调整列宽适应A4 A3 A2
                            • OpenCV计算机视觉实战(21)——模板匹配详解