You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言如何原地修改链表并返回头节点?LeetCode19题代码排查

问题描述

正在LeetCode平台完成19. 删除链表的倒数第N个结点题目,最初认为该题实现逻辑较为简单,编写的实现代码如下:

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
  int count = 1;
  struct ListNode* curr_node = head;
  while (curr_node->next) {
    curr_node = curr_node->next;
    count++;
  }
  int forward = count-n;
  count = 0;
  curr_node = head;
  while (count < forward) {
    curr_node = curr_node->next;
    count++;
  }
  struct ListNode * next_node = curr_node->next;
  struct ListNode final_before = *curr_node;
  final_before.next = next_node->next;
  return head;
}

但实际运行时上述代码完全没有修改传入的head节点关联的链表内容,核心疑问为:在C语言中应当如何对链表进行原地修改?

问题原因

代码完全不生效的核心错误是操作了临时副本,没有触碰原链表:

  • 代码中struct ListNode final_before = *curr_node;会把curr_node指向的原节点的全部内容,拷贝一份存储在栈上的临时变量final_before中,后续修改final_before.next仅仅是改动了这个临时拷贝的成员,原链表对应节点的next指针没有发生任何变化。函数执行结束后临时变量被系统回收,原链表自然不会有任何改动。
  • 原有逻辑还存在边界漏洞:当传入的n等于链表总长度时,需要删除的就是头节点本身,现有逻辑会访问空指针触发运行错误。
C语言链表原地修改方法

链表原地修改的核心原则非常明确:直接通过指针操作原节点的成员,不要对节点结构体做值拷贝后修改副本。如果需要修改某个节点的next指向,必须拿到该节点在内存中的真实地址(即指向该节点的指针),直接对指针指向的原结构体成员赋值。
针对这道题的正确实现步骤:

  • 第一次遍历链表统计总节点数,计算待删除节点从头开始的位置
  • 单独处理待删除节点为头节点的边界场景,直接返回头节点的next作为新链表头
  • 遍历定位到待删除节点的前驱节点,直接修改前驱节点的next指针,使其指向待删除节点的next,完成节点摘除

参考修正代码:

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
  int count = 0;
  struct ListNode* curr = head;
  // 统计链表总长度
  while (curr != NULL) {
    count++;
    curr = curr->next;
  }
  int remove_idx = count - n;
  // 处理删除头节点的边界情况
  if (remove_idx == 0) {
    return head->next;
  }
  // 定位到待删除节点的前驱节点
  curr = head;
  for (int i = 0; i < remove_idx - 1; i++) {
    curr = curr->next;
  }
  // 直接修改原节点的next指针,完成原地删除
  curr->next = curr->next->next;
  return head;
}

拓展:该题也可以用快慢指针实现单次遍历,不需要提前统计链表长度,本质逻辑完全一致,都是通过指针直接操作原节点成员,不存在节点结构体的拷贝。

内容的提问来源于stack exchange,提问作者zeroclaim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 19:12:19