Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K=3, then you must output 3→2→1→6→5→4; if K=4, you must output 4→3→2→1→5→6.
Input Specification:
Each input file contains one test case. For each case, the first line contains the address of the first node, a positive N (≤105) which is the total number of nodes, and a positive K (≤N) which is the length of the sublist to be reversed. The address of a node is a 5-digit nonnegative integer, and NULL is represented by -1.
Then N lines follow, each describes a node in the format:
Address Data Next
where Address
is the position of the node, Data
is an integer, and Next
is the position of the next node.
Output Specification:
For each case, output the resulting ordered linked list. Each node occupies a line, and is printed in the same format as in the input.
Sample Input:
00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
Sample Output:
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1
个人分析:题目乍一看题意很清晰:输入给出一个链表L(总结点数N),要求每K个结点将链表逆序。似乎菜鸡需要做的就是:根据输入建立一个链表,然后每K个结点逆序,再将新表的表头返回,按新表输出即可。
然而,上面一句如此简单的话,在实现过程中困难重重。
首先,是否真的需要建立C++指针链表?由于最大测试结点数Nmax已经给出,通过结构数组链表也能达到指针链表的效果。不过,虽然指针链表的代码较复杂,但在空间复杂度方面,数组链表一定是不如指针链表节约的(测试样例结点数<<Nmax)。进一步说,如果选择了结构数组链表,结构数组的形式该如何?是否真的需要每个结点设置3个参数(自身地址/自身值/下一结点地址)?还是说数组自身的下标也可以拿来表示上述三参数之一?
其次,如何实现每K个结点逆序?第一想法是在遍历原链表的过程中,每经过一个结点,就将该结点逆序。然而,如果仅仅是这样,那么就会丢失下一个结点的位置,如图:
所以,菜鸡一想,woc这不行啊...这不是要凉吗?!想想办法,想想办法.........要C点不丢.......欸?我拿个指针指着C不就行了
也就是说,在B还指着C的时候,在用一个curr指针,curr=B->Next不就行了嘛~(我怎么这么聪明?!低调低调)
最后,还有个问题:如何返回正确的头结点?如果只用上述的3个指针可能丢失所需要的头结点,如图:
虽然在这里好像菜鸡能在写代码之前就洞悉这些“陷阱”(都是假象= =),其实,菜鸡用了5个小时,磕磕绊绊,边写代码边debug边发现的这些问题......一把辛酸泪......不过这些都是小场面(比起在实验室做完全不动脑的体力实验,不知道好到哪去了,简直是幸福wow)
上代码!!
啊....等等!还有一个“陷阱”:陈越姥姥为了打击投机取巧凑数的同学,特别安排,初始输入的总结点数N>=链表结点数,也就是说会输入一些废结点,这些结点并不在所要处理的链表之中。所以,我们还需要在读入所有输入之后,数一下原序列到底有几个有效结点!
OjbK !!上代码!!!
#include<stdio.h> #define Max 100000 struct itemNode{ int data; int next; }; int Count(struct itemNode* L,int P) { int cum=1; for(P;L[P].next!=-1;P=L[P].next) { cum++; } // printf("cum%d\n",cum); return cum; } int Reverse(struct itemNode* List,int pList,int k,int num) { int prep,currp,nextp; //prep已逆转的最后一个节点,currp未逆转的第一个节点,currp之后的第一个节点 int head,lasthead; //head保存未逆序段落的旧头元素,lasthead保存逆序后的段落的尾节点 prep=-1; head=-1; currp=pList; //使初始 nextp=List[currp].next; for(int i=0;i<num/k;i++) //将原序列按逆序数K分为几个段落 { lasthead=head; //每次开始一个新段落逆序的时候,记录lasthead(上一段已逆序段落的尾节点) head=currp; //记录新段落(未逆序)的头结点 for(int j=0;j<k;j++) { List[currp].next=prep; prep=currp; currp=nextp; nextp=List[nextp].next; } if(i==0) { pList=prep; //逆序第一段的头节点 } else { List[lasthead].next=prep; //连接(已逆序)前一段的尾节点与(已逆序)后一段的头节点 } } List[head].next=currp; //连接将剩余的节点 return pList; } void PrintList(struct itemNode* List,int p) { for(p;List[p].next!=-1;p=List[p].next) { printf("%05d %d %05d\n",p,List[p].data,List[p].next); } printf("%05d %d %d\n",p,List[p].data,List[p].next); } int main() { //读入数据,建立数组链表 int pList,n,K; //pList指向头结点(即存头结点地址),n为总结点数,每隔K个结点反转。 scanf("%d %d %d",&pList,&n,&K); itemNode List[Max]; for(int i=0;i<n;i++) { int addr,data,next; scanf("%d %d %d",&addr,&data,&next); List[addr].data=data; List[addr].next=next; } //计算数组中属于链表的结点个数 int num=Count(List,pList); //逆序,返回逆序后链表的头节点 int newList=Reverse(List,pList,K,num); //输出逆序链表 PrintList(List,newList); return 0; }
测试结果:
总结:这道题,让菜鸡感受到了看似简单的题目,也会有很多耐人寻味的细节!以后再写代码之前,要尽量考虑各种边界情况,这能大大缩短debug的时间(泪目....Orz...debug时头有这么大)。再者,虽然经历了5个小时的探索->失败->失败->....->失败,最后重头看整个问题,豁然开朗,菜鸡觉得很值得。最后跟大家分享debug的时候听的歌词:
Fighting!!!