PTA(链表)1-8 求链表的倒数第m个元素(C)

1-8 求链表的倒数第m个元素

分数 5

作者 DS课程组

单位 浙江大学

请设计时间和空间上都尽可能高效的算法,在不改变链表的前提下,求链式存储的线性表的倒数第m(>0)个元素。

函数接口定义:

1
ElementType Find( List L, int m );

其中List结构定义如下:

1
2
3
4
5
6
typedef struct Node *PtrToNode;
struct Node {
ElementType Data; /* 存储结点数据 */
PtrToNode Next; /* 指向下一个结点的指针 */
};
typedef PtrToNode List; /* 定义单链表类型 */

L是给定的带头结点的单链表;函数Find要将L的倒数第m个元素返回,并不改变原链表。如果这样的元素不存在,则返回一个错误标志ERROR

裁判测试程序样例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <stdlib.h>

#define ERROR -1

typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode List;

List Read(); /* 细节在此不表 */
void Print( List L ); /* 细节在此不表 */

ElementType Find( List L, int m );

int main()
{
List L;
int m;
L = Read();
scanf("%d", &m);
printf("%d\n", Find(L,m));
Print(L);
return 0;
}

/* 你的代码将被嵌在这里 */

输入样例:

1
2
3
5
1 2 4 5 6
3

输出样例:

1
2
4
1 2 4 5 6

答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ElementType Find( List L, int m ) {
List fast = L->Next; // 定义一个快指针先走,从第一个结点开始
List slow = L->Next; // 定义一个慢指针,从第一个结点开始
int i = 0; // 定义一个计数器,让快指针先走m次

// 让快指针先走m次
while (i < m) {
if (fast == NULL) { // 防止m大于个数
return ERROR;
}
fast = fast->Next;
i++;
}
while (fast != NULL) {
slow = slow->Next;
fast = fast->Next;
}
return slow->Data;
}

思路

image-20251017140422875

这是典型的双指针(快慢指针)问题,用于查找链表中 “倒数第 m 个节点的值”:

  1. 快指针先向前移动 m 步
  2. 快慢指针同时向前移动,直到快指针到达链表末尾
  3. 此时慢指针指向的就是倒数第 m 个节点,返回其值

易错点

  1. 空链表或 m=0 处理:未判断L->Next是否为空(空链表),或 m 为 0 时直接操作,可能导致空指针访问
  2. m 超出链表长度:快指针移动 m 步前需检查是否提前遇到NULL,否则会越界
  3. 头节点处理:若链表带头节点,快慢指针需从L->Next(第一个数据节点)开始,而非L
  4. 返回值错误:当 m 无效时需返回题目规定的ERROR(需确保ERROR已定义)