【问题描述】 给定一个带头结点的单链表,删除单链表的倒数第k个节点。你可以假设测试数据保证k在合法的范围内。 【输入形式】 链表长度 链表(以空格区分) k 【输出形式】 链表(以空格区分) 【样例输入】 5 1 2 3 4 5 2 【样例输出】 1 2 3 5 【代码框架】 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #define ERROR 0 #define OK 1 #define TRUE 1 #define FALSE 0 typedef int ElemType; typedef int Status; // definition of node structure of singly linked list typedef struct L_node { ElemType data; // data field struct L_node *next; // pointer field }LNode, *LinkedList; //=========================================== // Create a singly linked list L with head node, and with n elements //=========================================== Status CreateList_L(LinkList &L, int a[ ], int n) { } //=========================================== // delete the kth element from the end //=========================================== void Delete_K(LinkList &L) { } //====================================== // Print the elements in a list //======================================= void LinkedListPrint(LinkedList L) { } int main() { }
时间: 2025-04-30 09:44:36 浏览: 40
<think>好的,我现在需要解决这个问题:删除单链表的倒数第k个节点。用户已经给了一个代码框架,我需要根据这个框架来补全代码。首先,我得仔细理解问题要求和输入输出格式。
题目给出的是一个带头结点的单链表,所以头结点是存在的,不需要处理空链表的情况,但要注意链表操作的一般步骤。删除倒数第k个节点,常见的思路是用快慢指针的方法。比如说,让一个指针先走k步,然后两个指针同时移动,当快指针到达末尾时,慢指针指向的就是倒数第k个节点的前驱节点,这样就能进行删除操作。
首先,我需要处理输入。输入的链表长度是n,然后是n个元素,接着是k。输出是删除后的链表。比如样例输入是5,1 2 3 4 5,k=2,所以删除倒数第二个节点,也就是4,输出1 2 3 5。
接下来看代码框架。有几个函数需要补全:CreateList_L,Delete_K,LinkedListPrint,以及主函数main。
首先,CreateList_L函数的作用是根据数组a和长度n创建链表。带头结点的单链表,所以应该先创建头结点,然后将数组中的元素依次插入到链表的尾部。例如,对于数组1 2 3 4 5,头结点后面接1,然后是2,依此类推。这个过程需要注意指针的移动,确保每个新节点正确链接。
然后,Delete_K函数是关键。需要找到倒数第k个节点并删除。这里要考虑k的有效性,但题目说明k在合法范围,所以不需要处理k过大或过小的情况。那么如何找到这个节点呢?快慢指针的方法比较高效。步骤如下:
1. 初始化两个指针,fast和slow,都指向头结点。
2. 让fast先移动k步。比如k=2时,fast先走到第二个节点。
3. 然后,同时移动fast和slow,直到fast到达最后一个节点(fast->next为NULL)。
4. 此时,slow指向的是倒数第k+1个节点,也就是要删除节点的前驱节点。这样,可以通过修改slow的next指针来删除目标节点。
例如,当链表是1->2->3->4->5,k=2时,fast先走到第2个节点(数据为2),然后fast和slow一起移动,直到fast到达最后一个节点(数据为5)。此时,slow位于数据3的位置,其next指向的是4,所以要删除4节点,需要将slow->next指向slow->next->next,并释放被删除节点的内存。
需要注意的是,如果链表长度为n,那么倒数第k个节点的位置是正数第n - k + 1的位置。但用快慢指针的方法不需要计算链表长度,只需要移动指针即可。
然后,在Delete_K函数中,需要注意头结点可能被操作的情况。例如,当要删除的是第一个节点(即倒数第n个节点,此时k等于链表长度),这时快指针移动k步后会到达NULL吗?或者可能到达最后一个节点的next?需要仔细分析。
假设链表长度是5,k=5,那么要删除的是第一个节点。此时,当fast先移动5步时,可能已经越过了链表末尾。例如,初始时fast和slow都指向头结点。然后让fast先移动5次,假设头结点不算在链表长度中(即链表长度是实际节点数),那么可能需要调整。或者链表长度是否包括头结点?题目中的链表长度输入是元素的数量,所以头结点不计入。例如样例输入中,链表长度是5,对应5个元素节点,头结点是额外的。
假设链表的结构是头结点->1->2->3->4->5。当k=5时,要删除的是第一个元素节点,即头结点的下一个节点。这时候快指针移动k次后的位置需要确认。例如,当快指针移动k次后,如果此时fast为NULL,则slow的下一个节点就是要删除的节点。
可能需要调整快指针的初始移动次数。例如,让快指针先移动k+1次,或者类似的调整。或者,可能需要让slow指向要删除节点的前驱,不管k的大小如何。例如,快指针先移动k次,然后快慢指针一起移动,直到快指针的下一个为NULL。这样,当k=5时,快指针移动5次后到达NULL,而slow此时位于头结点,这样slow的下一个节点就是第一个元素节点,也就是要删除的节点。这时候删除操作就是让头结点的next指向第二个节点。
比如,初始时fast和slow都指向头结点。快指针先移动k次(k=2)的例子:
头结点 ->1->2->3->4->5
fast先移动两次,从头结点到1,再到2?或者初始移动的次数是否需要调整?
可能我的思路有误,需要更仔细的分析。
假设头结点是L,链表结构是L -> 1 -> 2 ->3 ->4 ->5。此时,链表长度是5。当k=2时,要删除的是4这个节点。根据快慢指针的方法:
1. fast和slow都初始指向头结点L。
2. 让fast先移动k次(即两次),这样fast会移动到节点2?
或者,假设每移动一次是到下一个节点。比如,初始时,fast在头结点。移动一次到1,两次到2。此时,fast指向2。
3. 然后,fast和slow同时移动,直到fast的next是NULL。此时,fast会移动到最后一个节点5,此时fast->next为NULL。在这过程中,假设slow移动到节点3的位置。
此时,slow->next是4,也就是要删除的节点。于是,将slow->next指向5,并释放4节点。
这样,步骤正确。那在代码实现中,如何控制移动次数?
可能需要让fast先移动k次,然后slow和fast一起移动直到fast->next为NULL。这时,slow指向的是倒数k+1节点,即前驱节点。这就可以删除slow->next节点。
那具体代码步骤:
在Delete_K函数中:
- 初始化两个指针,slow和fast,都指向头结点。
- 移动fast指针k次。每次判断fast是否为空,但题目保证k合法,所以可以不用处理。
- 然后,同时移动slow和fast,直到fast->next为NULL。
- 此时,slow->next是要删除的节点。
- 保存slow->next为待删除节点。
- 将slow->next指向待删除节点的next。
- 释放该节点。
这样就能正确删除。
比如,当k等于链表长度时,假设链表长度为5,k=5:
- 移动fast指针5次。初始在头结点,移动一次到1,两次到2,三次到3,四次到4,五次到5。此时,fast指向5节点。
- 然后,检查fast->next是否为NULL。此时是的,所以停止移动。此时,slow仍然在头结点。
- 所以,slow->next是头结点的下一个节点,即第一个元素节点,需要删除。于是,将头结点的next指向第二个节点,并释放第一个节点。
这就能正确处理k等于链表长度的情况。
那这样的话,代码的逻辑是正确的。
现在,如何编写Delete_K函数?
函数参数是LinkList &L,也就是链表的头结点指针的引用。所以,函数内部可以直接操作L。
具体代码步骤:
void Delete_K(LinkList &L) {
int k; // 需要从输入获取k的值?
// 但原题中的输入形式是,在链表长度和链表元素之后输入k。所以在main函数中应该读取k,并传递给Delete_K函数?
这里发现用户提供的代码框架中的Delete_K函数是无参数的,而主函数中需要输入k的值。这说明原题中的代码框架可能有误,或者需要调整参数。
看用户提供的代码框架中的Delete_K函数声明是void Delete_K(LinkList &L),这说明该函数可能需要通过某种方式获取k的值,例如全局变量,或者原框架可能有错误。或者可能,用户给的代码框架可能有笔误,导致函数参数不正确。
这显然是一个问题。因为在主函数中,应该输入k的值,然后传递给Delete_K函数进行处理。而原框架中的Delete_K函数没有参数,无法接收k的值。所以,这可能是一个错误。
因此,我需要修改代码框架中的Delete_K函数,使其接受k作为参数。例如,函数应该定义为void Delete_K(LinkList &L, int k),这样主函数在读取k之后调用Delete_K(L, k)。
所以,用户的代码框架可能存在错误,可能需要调整函数参数。否则,无法正确处理不同的k值。例如,样例输入中的k=2,如何传递给Delete_K函数?
这个问题必须解决。假设用户提供的框架可能有误,我需要根据题意进行修正。否则,无法正确实现功能。
因此,在解决这个问题的时候,需要调整Delete_K函数的参数,使其能够接收k的值。否则,函数内部无法知道要删除倒数第几个节点。这说明原框架可能存在错误,或者我误解了框架的结构。
例如,可能原题中的代码框架可能有误,导致Delete_K函数的参数缺失。因此,在实现时,应该修改函数的定义,添加参数k。否则,该函数无法知道k的值。例如,原框架中的Delete_K函数应该是void Delete_K(LinkList &L, int k),然后在main函数中调用时传递k的值。
这可能是一个笔误或者代码框架的错误。因此,我必须根据实际情况进行调整,否则代码无法正确运行。
所以,正确的做法是修改Delete_K函数的参数,增加int k。然后在main函数中读取k后,调用Delete_K(L, k)。
这样,在用户提供的框架中,需要将void Delete_K(LinkList &L)改为void Delete_K(LinkList &L, int k),并修改相应的函数定义。
这一步非常关键,否则无法正确实现功能。所以,在补全代码的时候,需要调整函数参数。
所以,总结步骤:
1. 主函数中读取链表长度n,然后读取n个元素存入数组a,调用CreateList_L创建链表L。
2. 读取k的值。
3. 调用Delete_K(L, k)函数删除倒数第k个节点。
4. 调用LinkedListPrint输出链表。
因此,原框架中的Delete_K函数参数需要调整。
现在回到代码实现:
首先,CreateList_L函数:
参数是LinkList &L,int a[], int n。该函数需要创建带头结点的链表。例如:
Status CreateList_L(LinkList &L, int a[], int n) {
L = (LinkList)malloc(sizeof(LNode)); // 创建头结点
if (!L) return ERROR;
L->next = NULL;
LinkList tail = L;
for (int i = 0; i < n; i++) {
LinkList newNode = (LinkList)malloc(sizeof(LNode));
if (!newNode) return ERROR;
newNode->data = a[i];
newNode->next = NULL;
tail->next = newNode;
tail = newNode;
}
return OK;
}
这样,函数会正确创建链表。例如,输入数组1 2 3 4 5,n=5,则头结点后面依次链接这五个节点。
然后,Delete_K函数:
void Delete_K(LinkList &L, int k) {
LinkList fast = L;
LinkList slow = L;
// 让fast移动k次
for (int i = 0; i < k; i++) {
fast = fast->next;
// 题目保证k合法,所以不需要判断fast是否为空
}
// 现在,fast和slow同时移动,直到fast->next为NULL
while (fast->next != NULL) {
fast = fast->next;
slow = slow->next;
}
// 此时,slow的下一个节点是要删除的节点
LinkList delNode = slow->next;
slow->next = delNode->next;
free(delNode);
}
这个函数假设参数k已经被正确传入。例如,当k=2时,函数能正确找到倒数第二个节点并删除。
然后,LinkedListPrint函数:
void LinkedListPrint(LinkedList L) {
// L是头结点,所以第一个节点是L->next
LinkList p = L->next;
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
主函数部分:
int main() {
LinkedList L;
int n, k;
scanf("%d", &n);
int *a = (int *)malloc(n * sizeof(int));
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
CreateList_L(L, a, n);
scanf("%d", &k);
Delete_K(L, k);
LinkedListPrint(L);
// 释放链表内存,此处可能需要补充,但题目可能不要求
return 0;
}
但需要注意,原框架中的CreateList_L的参数是LinkList &L,也就是引用传递。在C语言中,这需要用指向指针的指针来实现。例如,在C语言中,函数参数传递如果是LinkList *L,或者用双重指针。原框架中的函数声明是Status CreateList_L(LinkList &L, int a[], int n),这在C语言中是错误的,因为C语言不支持引用。所以,用户提供的代码框架可能存在错误,应该使用指针的指针。
这可能导致编译错误。所以,必须修改函数参数,将LinkList &L改为LinkList *L,或者将函数声明改为指针形式。例如,C语言中正确的传递方式是使用LinkList *L。或者,用户可能将代码框架误写为C++的引用语法,而题目要求用C语言。
这是一个关键问题。例如,原框架中的函数参数是Status CreateList_L(LinkList &L, int a[], int n),这在C语言中是无效的,必须改为LinkList *L。否则代码无法编译。
这可能是一个框架的错误,或者用户的问题描述中的笔误。比如,用户提供的代码框架是C++的,而题目要求用C语言实现?
这里存在矛盾。因为C语言不支持引用传递,所以正确的做法是使用指针的指针。例如:
Status CreateList_L(LinkList *L, int a[], int n) {
*L = (LinkList)malloc(sizeof(LNode));
// ...其他操作
}
但原框架中使用的是C的头文件,如#include <stdio.h>等,所以必须用C语言语法。因此,原来的函数参数中的引用符号是错误的,必须调整。
这说明原框架存在错误,需要修正。否则代码无法在C语言中编译通过。这可能是一个笔误,正确的代码框架应该使用指针的指针。
因此,在补全代码的时候,需要将函数的参数调整为LinkList *L,例如:
Status CreateList_L(LinkList *L, int a[], int n) {
*L = (LinkList)malloc(sizeof(LNode));
if (*L == NULL) return ERROR;
(*L)->next = NULL;
LinkList tail = *L;
for (int i = 0; i < n; i++) {
LinkList newNode = (LinkList)malloc(sizeof(LNode));
if (!newNode) return ERROR;
newNode->data = a[i];
newNode->next = NULL;
tail->next = newNode;
tail = newNode;
}
return OK;
}
在主函数中调用时,应该是:
CreateList_L(&L, a, n);
同样,Delete_K函数的参数如果是LinkList &L,在C语言中必须改为LinkList *L。或者,在C语言中,如果函数需要修改链表头指针,可能需要传递指针的指针。但在这个问题中,删除的节点不会是头结点本身(头结点不会被删除,只是头结点之后的节点可能被删除),所以链表头指针L的值不需要改变,只需要修改其next指针。因此,函数参数可以传递LinkList L,而不需要引用或指针。例如,原Delete_K函数的参数如果是LinkList L,也可以操作链表。但原框架中的参数是LinkList &L,这可能在C++中是允许的,但在C中无法使用。
这导致用户提供的代码框架中存在语法错误,无法直接编译。因此,我必须根据C语言的语法来调整函数参数,或者用户可能希望我们在代码中忽略这个错误,假设函数参数正确。
这是一个比较严重的问题,必须解决。否则,代码无法正确运行。
现在,我需要假设用户提供的框架是正确的,或者可能这实际上是C++代码的框架,而用户希望用C++实现。但题目中的头文件是C的头文件,例如#include <stdio.h>等,所以这可能矛盾。
或者,可能用户提供的代码框架中的LinkList被定义为指针类型,所以LinkList &L相当于LNode**类型?例如,如果LinkList是typedef的LNode*,那么LinkList &L相当于LNode* &L,这在C++中是允许的,但在C中不行。因此,用户可能提供的框架是C++的,而问题描述中可能希望用C++实现。
例如,原框架中的函数参数使用引用,说明代码是用C++编译的。因此,可能问题中的代码框架是C++的,而不是C语言的。例如,C++中的引用参数是允许的,所以函数参数中的LinkList &L是正确的。
这可能是一个关键点。例如,原题中的代码框架实际上是C++的,而不是C语言的。例如,在C++中,使用引用参数是允许的,所以函数CreateList_L的参数写成LinkList &L是合法的。因此,用户可能希望代码用C++编写,但头文件中包含的是C的标准库头文件。例如,虽然代码使用了C++的引用,但头文件是C的,这可能在某些编译器中允许。
如果这样,那么代码框架是正确的,可以用C++编译。因此,在补全代码的时候,需要按照C++的语法来处理,例如使用引用参数。
因此,假设代码框架是C++的,那么函数参数是正确的,可以正常使用。
因此,主函数中的调用应该是:
CreateList_L(L, a, n);
在C++中,这会将L的引用传递给函数,函数内部对L的修改会反映到主函数的L变量中。
因此,回到CreateList_L函数:
Status CreateList_L(LinkList &L, int a[], int n) {
L = (LinkList)malloc(sizeof(LNode)); // 创建头结点
if (!L) return ERROR;
L->next = NULL;
LinkList tail = L; // tail指向头结点
for (int i = 0; i < n; i++) {
LinkList newNode = (LinkList)malloc(sizeof(LNode));
if (!newNode) {
// 如果分配失败,可能需要释放已分配的节点
return ERROR;
}
newNode->data = a[i];
newNode->next = NULL;
tail->next = newNode;
tail = newNode;
}
return OK;
}
这样,头结点被正确创建,并且后续节点被链接。
然后,Delete_K函数:
void Delete_K(LinkList &L, int k) { // 这里的参数应该添加k,否则无法处理不同的k值
LinkList fast = L;
LinkList slow = L;
for (int i = 0; i < k; ++i) {
fast = fast->next;
}
while (fast->next != NULL) {
fast = fast->next;
slow = slow->next;
}
LinkList delNode = slow->next;
slow->next = delNode->next;
free(delNode);
}
但原框架中的Delete_K函数声明是void Delete_K(LinkList &L),没有参数k。这说明原框架中的函数参数是不正确的,必须修改。因此,在用户提供的代码框架中,存在错误,导致无法正确传递k的值。所以,我必须调整函数声明,添加参数k。
所以,在用户提供的代码框架中,函数声明需要改为:
void Delete_K(LinkList &L, int k);
然后在主函数中调用Delete_K(L, k);
否则,原框架中的函数无法正确接收k的值,导致无法删除正确的节点。
因此,这一步是必须的。这说明用户提供的代码框架可能存在错误,需要修正。
因此,在补全代码时,我需要调整Delete_K函数的参数,使其能够接收k的值。
现在,假设我已经调整了Delete_K函数的参数,那么主函数中的调用是正确的。
现在,主函数的实现:
int main() {
LinkedList L; // 头结点指针
int n;
scanf("%d", &n);
int *a = (int *)malloc(n * sizeof(int));
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
CreateList_L(L, a, n); // 创建链表,L被传入并被赋值
int k;
scanf("%d", &k);
Delete_K(L, k); // 删除倒数第k个节点
LinkedListPrint(L); // 输出链表
// 释放动态分配的内存
// 这里可能需要编写链表释放的函数,但题目可能不要求
free(a);
// 释放链表节点
LinkedList p = L;
while (p != NULL) {
LinkedList temp = p;
p = p->next;
free(temp);
}
return 0;
}
这样,整个程序的流程是正确的。
现在,需要验证代码的正确性,例如样例输入:
输入:
5
1 2 3 4 5
2
处理过程:
CreateList_L将创建链表头结点,然后依次链接1、2、3、4、5。
Delete_K(L, 2):
fast和slow初始都指向头结点。fast移动2次,到达节点2。然后,fast和slow同时移动,直到fast->next为NULL。此时,fast在5节点,slow在3节点。删除slow->next(即4节点),链表变为1->2->3->5。
输出正确。
另一个测试用例:当k等于链表长度,如k=5:
链表是1->2->3->4->5,删除倒数第5个节点,即1。此时,fast移动5次,从头结点开始:
第一次移动:头结点->next是1节点,fast移动到1。
第二次:2节点。
第三次:3.
第四次:4.
第五次:5.
fast现在指向5节点。此时,fast->next为NULL吗?不是,5节点的next是NULL。所以,在循环条件中,fast->next != NULL?此时,fast是5节点,它的next是NULL。所以循环不会执行。slow仍然在头结点。此时,slow的next是1节点,删除它。这样,头结点的next指向2节点。链表变为2->3->4->5。输出正确。
因此,代码逻辑是正确的。
综上,需要补全的代码步骤如下:
1. CreateList_L函数创建带头结点的链表,使用尾插法。
2. Delete_K函数使用快慢指针找到倒数第k个节点的前驱,删除该节点。
3. 主函数正确读取输入,调用各函数,并输出结果。
现在,将代码补全到用户提供的框架中,注意函数参数是否调整。
但用户提供的框架中的Delete_K函数没有参数k,所以必须修改框架中的函数声明。这可能是一个问题,但如果按照用户提供的框架,无法正确实现功能。因此,必须调整函数声明。
但用户的问题描述中说“代码框架”,可能允许修改函数的参数。所以,我需要在补全代码时进行必要的调整,以确保功能正确。
因此,最终的代码应该包括对函数参数的调整。
现在,将各部分代码补全:
CreateList_L的实现:
Status CreateList_L(LinkList &L, int a[], int n) {
L = (LinkList)malloc(sizeof(LNode)); // 创建头结点
if (!L) return ERROR;
L->next = NULL;
LinkList tail = L;
for (int i = 0; i < n; i++) {
LinkList newNode = (LinkList)malloc(sizeof(LNode));
if (!newNode) {
// 创建失败,释放之前分配的内存
LinkList p = L;
while (p != NULL) {
LinkList temp = p;
p = p->next;
free(temp);
}
return ERROR;
}
newNode->data = a[i];
newNode->next = NULL;
tail->next = newNode;
tail = newNode;
}
return OK;
}
Delete_K的实现:
void Delete_K(LinkList &L, int k) {
LinkList fast = L;
LinkList slow = L;
// 快指针先移动k次
for (int i = 0; i < k; i++) {
fast = fast->next;
}
// 移动slow和fast,直到fast的下一个为空
while (fast->next != NULL) {
fast = fast->next;
slow = slow->next;
}
// 删除slow的下一个节点
LinkList delNode = slow->next;
slow->next = delNode->next;
free(delNode);
}
但是,原框架中的Delete_K函数定义是没有参数k的,所以需要修改。因此,在代码中,需要将函数定义改为:
void Delete_K(LinkList &L, int k) { ... }
否则,无法接收k的值。
主函数部分:
int main() {
LinkedList L;
int n;
scanf("%d", &n);
int *a = (int*)malloc(n * sizeof(int));
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
CreateList_L(L, a, n); // 创建链表,传入L的引用
int k;
scanf("%d", &k);
Delete_K(L, k); // 删除操作
LinkedListPrint(L);
// 释放内存
free(a);
// 释放链表
while (L != NULL) {
LinkedList temp = L;
L = L->next;
free(temp);
}
return 0;
}
需要注意的是,在释放链表内存时,头结点也需要被释放。
LinkedListPrint的实现:
void LinkedListPrint(LinkedList L) {
LinkList p = L->next; // 跳过头结点
while (p != NULL) {
printf("%d", p->data);
if (p->next != NULL) {
printf(" ");
}
p = p->next;
}
printf("\n");
}
这样可以正确输出,每个元素之间用空格分隔,最后换行。
综上,整个代码的补全需要调整函数参数,确保k被正确传递,并使用快慢指针方法删除倒数第k个节点。测试样例可以得到正确结果。</think>要删除单链表的倒数第k个节点,可以使用快慢指针的方法。以下是具体的实现步骤和代码:
### 步骤解析
1. **创建链表**:使用头插法或尾插法创建带头结点的单链表。
2. **快慢指针定位**:
- 初始化快指针 `fast` 和慢指针 `slow`,均指向头结点。
- 快指针先移动 `k` 步。
- 然后同时移动快慢指针,直到快指针到达链表末尾。
- 此时慢指针指向倒数第 `k+1` 个节点,即待删除节点的前驱。
3. **删除节点**:调整指针并释放内存。
### 完整代码
```c
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define ERROR 0
#define OK 1
#define TRUE 1
#define FALSE 0
typedef int ElemType;
typedef int Status;
typedef struct L_node {
ElemType data;
struct L_node *next;
} LNode, *LinkedList;
Status CreateList_L(LinkedList *L, int a[], int n) {
*L = (LinkedList)malloc(sizeof(LNode));
if (*L == NULL) return ERROR;
(*L)->next = NULL;
LinkedList tail = *L;
for (int i = 0; i < n; i++) {
LinkedList newNode = (LinkedList)malloc(sizeof(LNode));
if (!newNode) {
LinkedList p = *L;
while (p != NULL) {
LinkedList temp = p;
p = p->next;
free(temp);
}
return ERROR;
}
newNode->data = a[i];
newNode->next = NULL;
tail->next = newNode;
tail = newNode;
}
return OK;
}
void Delete_K(LinkedList *L, int k) {
LinkedList fast = *L;
LinkedList slow = *L;
for (int i = 0; i < k; i++) {
fast = fast->next;
}
while (fast->next != NULL) {
fast = fast->next;
slow = slow->next;
}
LinkedList delNode = slow->next;
slow->next = delNode->next;
free(delNode);
}
void LinkedListPrint(LinkedList L) {
LinkedList p = L->next;
while (p != NULL) {
printf("%d", p->data);
if (p->next != NULL) {
printf(" ");
}
p = p->next;
}
printf("\n");
}
int main() {
LinkedList L;
int n, k;
scanf("%d", &n);
int *a = (int *)malloc(n * sizeof(int));
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
CreateList_L(&L, a, n);
scanf("%d", &k);
Delete_K(&L, k);
LinkedListPrint(L);
free(a);
while (L != NULL) {
LinkedList temp = L;
L = L->next;
free(temp);
}
return 0;
}
```
### 代码说明
1. **链表创建**:`CreateList_L` 使用尾插法创建带头结点的链表,保证元素顺序正确。
2. **节点删除**:`Delete_K` 通过快慢指针定位到倒数第 `k` 个节点的前驱,直接修改指针完成删除。
3. **输入输出处理**:主函数读取输入数据并调用相关函数,最后释放内存避免泄漏。
### 关键点
- **快慢指针**:快指针先走 `k` 步,确保慢指针最终定位到正确的前驱节点。
- **边界处理**:当 `k` 等于链表长度时,直接删除头结点的下一个节点(首节点)。
- **内存管理**:动态分配的内存在使用后需手动释放,避免内存泄漏。
阅读全文
相关推荐

















