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

Leetcode 24

1 题目

876. 链表的中间结点

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

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

示例 1:

输入:head = [1,2,3,4,5]
输出:[3,4,5]
解释:链表只有一个中间结点,值为 3 。

示例 2:

输入:head = [1,2,3,4,5,6]
输出:[4,5,6]
解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。

提示:

  • 链表的结点数范围是 [1, 100]
  • 1 <= Node.val <= 100

2 解法

(c语言实现)

法一:快慢指针法

思路

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

    复杂度

    • 时间复杂度: O(n) O(n) O(n)
    • 空间复杂度: O(1) O(1) 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;
    }

    【踩坑】:注意位与&和逻辑与&&的区别

    这个函数用于查找单链表的中间节点,采用了 "快慢指针" 的经典算法。让我们详细分析其逻辑:

    1. 算法核心思想

    使用两个指针遍历链表:

    • 慢指针 (slow):每次移动 1 步
    • 快指针 (fast):每次移动 2 步

    当快指针到达链表末尾时,慢指针恰好指向链表的中间节点。

    2. 循环条件分析:fast != NULL && fast->next != NULL

    这是一个逻辑与操作,需要两个条件同时满足才会执行循环体:

    • 第一个条件fast != NULL:确保快指针没有超出链表范围
    • 第二个条件fast->next != NULL:确保快指针的下一个节点存在
    条件执行顺序

    C 语言中逻辑与&&运算符具有短路特性

    1. 先判断第一个条件fast != NULL
    2. 只有当第一个条件为真时,才会判断第二个条件fast->next != NULL
    3. 如果第一个条件为假,直接跳过第二个条件的判断

    这种执行顺序非常重要,它避免了对空指针进行->next操作,防止程序崩溃。

    3. 为什么不能用位与&

    如果将逻辑与&&换成位与&,会导致严重问题:

    1. 逻辑错误:位与是对两个值的二进制位进行与操作,而不是判断两个条件是否同时成立
    2. 空指针访问:位与不具备短路特性,无论第一个条件是否为真,都会执行第二个条件的判断。当fastNULL时,执行fast->next会导致空指针访问,程序崩溃

    例如,当fastNULL时:

    • 使用&&:因第一个条件为假,不执行第二个条件,安全
    • 使用&:会执行fast->next,导致未定义行为(通常是程序崩溃)

    4. 算法执行过程

    • 对于偶数个节点:当快指针到达最后一个节点时,慢指针在中间两个节点的第二个节点
    • 对于奇数个节点:当快指针到达末尾 (NULL) 时,慢指针在正中间节点

    这种算法的时间复杂度是 O (n),空间复杂度是 O (1),是查找链表中间节点的最优解法。

    法二:计数法

    思路

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

      复杂度

      • 时间复杂度: O(n) O(n) O(n)
      • 空间复杂度: O(1) O(1) O(1)
      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;
      }

      【踩坑】:循环条件

      我写了一个错误的代码。

      /*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
      typedef struct ListNode ListNode;
      struct ListNode* middleNode(struct ListNode* head) {ListNode *cur = head;int count = 0;while(cur != NULL && cur -> next != NULL){count ++;cur = cur -> next;}int mid = count /2 ;ListNode *midNode = head;while(mid --){midNode = midNode -> next;}return midNode;}

      这段代码的目的是找到链表的中间节点,但实现了逻辑错误,会导致在某些情况下返回错误的结果。我们来详细分析:

      问题分析

      代码采用了 "先计数再查找" 的思路,但计数逻辑存在缺陷:

      1. 计数错误

        while(cur != NULL && cur -> next != NULL){count ++;cur = cur -> next;
        }
        

        这个循环的终止条件有问题,导致count值不能正确反映链表的实际长度:

        • 对于 n 个节点的链表,实际会少统计 1 个节点
        • 例如:3 个节点的链表只会计数 2 次
        • 例如:4 个节点的链表只会计数 3 次
      2. 中间位置计算错误由于计数错误,导致mid = count / 2计算出的中间位置也是错误的:

        • 当链表有 3 个节点时,count 会得到 2,mid=1,最终返回第 2 个节点(正确)
        • 当链表有 4 个节点时,count 会得到 3,mid=1,最终返回第 2 个节点(错误,应该返回第 3 个)
        • 当链表有 5 个节点时,count 会得到 4,mid=2,最终返回第 3 个节点(正确)
        • 当链表有 2 个节点时,count 会得到 1,mid=0,最终返回第 1 个节点(错误,应该返回第 2 个)

      错误原因

      根本问题在于循环的终止条件:cur != NULL && cur -> next != NULL这个条件会在遍历到倒数第二个节点时就停止,导致最后一个节点没有被计数。

      修正方案

      应该先完整统计链表长度,再计算中间位置:

      struct ListNode* middleNode(struct ListNode* head) {if (head == NULL) return NULL;ListNode *cur = head;int count = 0;// 修正1:正确统计所有节点while(cur != NULL) {count++;cur = cur->next;}// 修正2:正确计算中间位置int mid = count / 2;ListNode *midNode = head;while(mid--) {midNode = midNode->next;}return midNode;
      }
      

      为什么原代码会这样

      原代码的作者可能混淆了两种遍历方式:

      1. 想统计节点总数,却用了类似 "快慢指针" 的终止条件
      2. 没有考虑到循环条件对计数结果的影响
      3. 没有测试偶数个节点的情况

      这种错误在链表操作中很常见,尤其当涉及到边界条件处理时,需要仔细考虑各种长度的链表情况。

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

      相关文章:

    • 后缀学习笔记 | -ability -ibility 系列
    • 若依使用基本步骤
    • win7winlogon完整调试流程
    • SSM高校图书馆网站m7o77(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
    • 网站建设 技术团队建设工程施工合同最新版本
    • AX520CE-- 音视频mdk的初识
    • 状态设计_多重集排列数_剪枝
    • adt-bundle-windows
    • Bootstrap 5入门指南
    • 奥林巴斯读片软件OlyVIA 2.9 下载安装教程怎样下载安装图文教程
    • 18006.STM32通过SPI读取LAN9253数据
    • 无锡网站建设维护梅州建站教程
    • SQLSugar和EF都是数据优先吗?
    • 企业网站功能是什么百度怎么发布网站
    • 八股-2025.10.12
    • Eigen 曲线拟合之四阶多项式(有界约束,投影法)
    • 深度剖析 C++ 之 vector(下)篇
    • Vue计算属性与监视
    • 零基础学AI大模型之解析器PydanticOutputParser
    • Linux 命令 —— 常用命令总结
    • 【AI论文】大型推理模型能从有缺陷的思维中更好地习得对齐能力
    • 网站服务器费用wordpress手赚推广
    • 24ICPC昆明站补题
    • 口碑好的聊城网站建设设计软件网站
    • 五种编程语言比较选择最适合您项目的工具
    • 商城网站开发项目分工公司网页背景图
    • 第六章:并发编程—Go的杀手锏
    • 网站建设内部流程图定制开发网站 推广
    • 衡石科技HENGSHI SENSE 6.0:重塑企业级BI的技术范式
    • 西安便宜网站建设品牌网十大品牌排行榜