PAT乙1025:反转链表

问题描述:

给定一个常数 K 以及一个单链表 L,请编写程序将 L 中每 K 个结点反转。例如:给定 L 为 1→2→3→4→5→6,K 为 3,则输出应该为 3→2→1→6→5→4;如果 K 为 4,则输出应该为 4→3→2→1→5→6,即最后不到 K 个元素不反转。

输入格式:

每个输入包含 1 个测试用例。每个测试用例第 1 行给出第 1 个结点的地址、结点总个数正整数 N (≤10​5​​)、以及正整数 K (≤N),即要求反转的子链结点的个数。结点的地址是 5 位非负整数,NULL 地址用 −1 表示。

接下来有 N 行,每行格式为:

Address Data Next

其中 Address 是结点地址,Data 是该结点保存的整数数据,Next 是下一结点的地址。我的博客

输出格式:

对每个测试用例,顺序输出反转后的链表,其上每个结点占一行,格式与输入相同。

输入样例:

00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218

输出样例:

00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1

问题分析:

首先,根据题目的设定,我们要考虑创建一个链表节点的结构体进行存储链表节点的数据,一整个链表就用这个结构体数组进行存储即可。由于录入的数据是一个链表,所以我们在录入的过程中有些技巧,不能将录入的顺序号作为结构体数组的下标,而应该直接用地址作为结构体的下标,这样便于进行排序。

这题在解题上有2种思路,一种是数组思路,一种是链表思路。如果用数组的方式去求解这题,需要额外再创建一个节点的数组指针,先把所有的节点按照顺序排好,然后再翻转。

个人理解,还是用数组的方式处理这个问题更加的高效。实现的方式直接见代码就可以了,非常的清晰明了。

关于链表法的总体思路是,先把数据读进来,然后写一个针对链表翻转的函数,函数功能如下:

第一个节点的next指针开始时不动,第一个节点往后的next指针指向前一个节点(这里有点绕),一直轮询K次(即开始时输入的K的值),此时已得到这段需要翻转的链表的最后一个节点的next,先存下next的参数,然后把next指向前一个节点,此时,再将第一个节点的next值改为最后一个节点的next值,此时这一段链表已完成整个翻转过程。链表的方式处理这个问题还是有点繁琐的。

这题的测试点有一个坑:链表的节点数不一定是N个。题目并没有说这N个节点都是属于链表的,言外之意就是,可能有几个节点不在这个链表上,需要考虑这个问题。

最后说一下,在考试时候,肯定是考虑使用数组的方式进行求解,这种方式的代码量相对较少,而且思路非常的清晰,很快可以把问题进行处理。如果想把链表这个数据结构学习的扎实一点,不妨用链表改指针的方式把这道题给求解出来,不过链表法用起来思路比较的乱,因为指针比较的多,中间的temp变量也多,容易把思路弄混乱,但是可以训练解决数据结构中链表问题的能力。

数组方式求解的代码:

#pragma warning(disable:4996)
#include <stdio.h>
#include <algorithm>
using namespace std;

struct node
{
	int add;
	int data;
	int next;//新的链表下一个节点的地址
};
node List[100010];
node* Plist[100010];//定义一个指针数组,指向List

int main()
{
	int  first, step=0;
	int N, K;
	scanf("%d%d%d", &first, &N, &K);
	int add, data, next;
	
	for (int i = 0; i < N; i++)
	{
		scanf("%d%d%d", &add, &data, &next);
		List[add].add = add;
		List[add].data = data;
		List[add].next = next;
	}

	while (first !=-1)
	{
		Plist[step] = &List[first];
		first = List[first].next;
		step++;//节点的数量为step最后的值
	}

	for (int i = 0; i < (step - step % K); i += K)//数组指针进行倒序排列
		reverse(Plist + i, Plist + i + K);

	for (int i = 0; i < step-1; i++)//输出
		printf("%05d %d %05d\n", Plist[i]->add, Plist[i]->data, Plist[i+1]->add);
	printf("%05d %d -1", Plist[step-1]->add, Plist[step-1]->data);//输出最后一个参数

	return 0;
}

链表方式求解的代码:

#pragma warning(disable:4996)
#include <string.h>
#include <stdio.h>
using namespace std;

struct node
{
	int data;
	int nextOld;//原先链表下一个节点的地址
	int next;//新的链表下一个节点的地址
};
node List[100010];
/*
备注:这里所说的指针并不是指针类型,而是指向数组的下标。指针不一定非要用指针类型进行表示。
*/
int reverse(node list[], int k, int first)//k为节点数,p为一段链表中首地址,返回翻转后最后一个地址
{
	int add, next;
	int pointer;//指向当前位置的指针
	//首节点操作与其他节点操作有所不同

	add = first;//add保存指针pointer所指的前一个地址
	pointer = list[first].next;

	for (int i = 1; i < k; i++)//从第二个节点进行操作
	{
		next = list[pointer].next;//将链表当前节点的下一个地址进行保存在temp2
		list[pointer].next = add;//链表指针指向前一个地址
		add = pointer;//地址改为当前指针所指的节点
		pointer = next;//指针指向下一个节点
	}

	list[first].next = pointer;//首节点指向下一个
	return add;//返回当前段链表最后的一个地址
}


int main()
{
	int  first, temp, step=0;
	int temp2,temp3;
	int N, K;
	scanf("%d%d%d", &first, &N, &K);
	int add, data, next;

	for (int i = 0; i < N; i++)
	{
		scanf("%d%d%d", &add, &data, &next);
		List[add].data = data;
		List[add].nextOld = next;
		List[add].next = next;
	}

	temp = first;
	while (temp!=-1)
	{
		temp = List[temp].next;
		step++;
	}

	temp = first;//存上一个链表原先第一个节点
	first = reverse(List, K, temp);//头节点的地址变成第一个链表中最后一个节点的地址
	temp2 = first;

	for (int i = 2 * K; i <= step; i += K)
	{
		temp2 = List[temp2].nextOld;//往后移一位
		temp3 = temp2;
		temp2 = reverse(List, K, temp2);
		List[temp].next = temp2;
		temp = temp3;
	}

	while (List[first].next!=-1)
	{
		printf("%05d %d %05d\n", first, List[first].data, List[first].next);
		first = List[first].next;
	}
	printf("%05d %d -1", first, List[first].data);

	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值