菜鸡奋斗路02-线性结构3 Reversing Linked List

Given a constant K and a singly linked list L, you are supposed to reverse the links of every 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
作者: 陈越
单位: 浙江大学
时间限制: 400ms
内存限制: 64MB
代码长度限制: 16KB


个人分析:题目乍一看题意很清晰:输入给出一个链表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!!!

猜你喜欢

转载自blog.csdn.net/qq_41829562/article/details/80258425