题目:
本题要求实现一个函数,将给定的单链表逆转。
函数接口定义:
List Reverse( List L );
其中List
结构定义如下:
typedef struct Node *PtrToNode;
struct Node {
ElementType Data; /* 存储结点数据 */
PtrToNode Next; /* 指向下一个结点的指针 */
};
typedef PtrToNode List; /* 定义单链表类型 */
L
是给定单链表,函数Reverse
要返回被逆转后的链表。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode List;
List Read(); /* 细节在此不表 */
void Print( List L ); /* 细节在此不表 */
List Reverse( List L );
int main()
{
List L1, L2;
L1 = Read();
L2 = Reverse(L1);
Print(L1);
Print(L2);
return 0;
}
/* 你的代码将被嵌在这里 */
输入样例:
5
1 3 4 5 2
输出样例:
1
2 5 4 3 1
[分析]:
要实现链表的逆转,最容易想到的就是依次读取,用头插法的方式写入新的链表。
[踩到的坑]:
测试代码时发现本题链表为无头结点链表。
未注意输出样例,逆转链表的同时原链表也发生改动。
思路及最终结果:
//创建带头结点的新链表,依次读取输入的链表,头插法写入新链表,最后将新链表头结点删除。
List Reverse( List L ){
List l=(List*)malloc(sizeof(List));
l->Next=NULL;
List temp=L; //保存原本链表
while(temp!=NULL){
List temp1=(List*)malloc(sizeof(List));
temp1->Data=temp->Data;
temp1->Next=l->Next;
l->Next=temp1;
temp=temp->Next;
}
l=l->Next;
return l;
}
/**********运行结果************
1 3 4 5 2
2 5 4 3 1
*/
从运行结果来看,该段代码的确实现了将链表逆转,但是只要你足够粗心,你就不会发现该运行结果与题目所给输出样例有所区别。提交代码,成功出错。
题中样例输出的L1很明显只剩下第一个数据,其他数据都跑到了L2,那么如果将L1的数据用头插法赋值给新的链表,明显对于L1不会有任何的变动,那么不通过赋值的方式,直接将L1的节点接续到L2后面是否能够实现该结果呢?同时需要保证L1的第一个数据最后输出,那么明显是从后往前读,依次将节点移到另一个链表;或者从前往后读,但不管哪种操作,第一个节点都为复制(非剪切)。这两种方法的实现太过于凑巧。
List Reverse( List L )
{
List p1=NULL,p2=NULL;
while (L)
{
p2=L->Next;
L->Next=p1;
printf("1:");
Print(L);
p1=L;
L=p2;
printf("2:");
Print(L);
}
return p1;
}
/**********运行结果************
1:1
2:3 4 5 2
1:3 1
2:4 5 2
1:4 3 1
2:5 2
1:5 4 3 1
2:2
1:2 5 4 3 1
2:Empty
1
2 5 4 3 1
由网上给出的代码添加输出可以发现输入链表最后为空,说明该检验代码与正常逻辑存在一定的偏颇,如果非要纠结一个固定结果,难免在此题中深陷,私以为掌握链表逆转方法逻辑比在此题中获取满分更重要。(对于本题的结果,可以在最后加上一个判断补丁即可获得满分)
List Reverse( List L ){
List l=(List*)malloc(sizeof(List));
l->Next=NULL;
List temp=L;
while(temp!=NULL){
List temp1=(List*)malloc(sizeof(List));
temp1->Data=temp->Data;
temp1->Next=l->Next;
l->Next=temp1;
temp=temp->Next;
}
l=l->Next;
if(L!=NULL){
L->Next=NULL;
}
return l;
}
后记:
最终还是坚持自己的代码思路而非采用网上的代码是有个人的考虑的,私以为,无论对数据作何操作,原数据能保存最好(备份),当然该代码可用是能够再次将链表逆转回来的,这也就涉及到了时间换空间还是空间换时间的选择。