PAT乙级 1025 反转链表(带解析,带测试点6的坑点)

题目:

给定一个常数 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 (≤105)、以及正整数 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

解析:

需要解决两个问题:

1.什么样的数据结构存储链表

2.怎样翻转链表

利用哈希的思想来存储链表:

        创建如下的结构体数组来存储链表:

const int N = 1e5+5;
struct node{
	int id;//数值 
	int next;//下一个地址 
}ns[N];

        节点在数组中的下标代表它的地址,它的数值和下一地址在结构体中存储:

//存储节点 
for(int i=0;i<n;i++)
{
	int a,b,c;//地址,id,next 
	cin>>a>>b>>c;
	ns[a].id = b;
	ns[a].next = c;
}

           就比如本题给出的输入样例,它在哈希表中的存储大概是这样的:

            因此,我们要找某个地址的节点,直接找ns[地址]即可

关于翻转链表的讨论:

        

 由图可知,把每段要翻转的链表分成一个单独的子链表,那么非开头的节点只需要把它的next属性指向它的前一个节点即可,(如何找它的前一个节点可以创建一个辅助数组s[N],顺序记录链表的每一个节点的地址);

变数在于每段子链表的第一个节点,如果它的后面没有需要翻转的子链表了,那么可以直接把他的next指向该子链表最后一个节点的next,如本图的4 5 6,可以直接把4的next指向6的next也就是7;但是如果该子链表的后面还有需要翻转的子链表,如本土的1 2 3,如果现在把1指向了4,等后面的4 5 6翻转之后,4将不再是它所属的子链表的第一个节点了,所以1应该指向6;

翻转函数如下:

int s[N];//顺序记录每个节点的地址 
int lun;//需要翻转多少论(n/k轮,最后不足k个不反转) 
int k;//每k个节点之间需要翻转 
void invers(int st,int ed,int i)//把st 和 ed 之间的链表翻转 ,i代表是第i段子链表 
{
	//每段子链表的头一个节点分情况处理 
	if(i==lun-1)//最后一轮了 (lun表示一共有lun段需要反转的子链表)
		ns[s[st]].next = ns[s[ed]].next;
	else
		ns[s[st]].next = s[ed+k];
	//每段子链表的非头一个节点常规处理 
	for(int i=ed;i>st;i--)
	{
		ns[s[i]].next = s[i-1];
	}
}

代码:

#include<iostream>
#include<iomanip>
using namespace std;
const int N = 1e5+5;
struct node{
	int id;//数值 
	int next;//下一个地址 
}ns[N];
int s[N];//顺序记录每个节点的地址 
int lun;//需要翻转多少论(n/k轮,最后不足k个不反转) 
int k;//每k个节点之间需要翻转 
void invers(int st,int ed,int i)//把st 和 ed 之间的链表翻转 ,i代表是第i段子链表 
{
	//每段子链表的头一个节点分情况处理 
	if(i==lun-1)//最后一轮了 (lun表示一共有lun段需要反转的子链表)
		ns[s[st]].next = ns[s[ed]].next;
	else
		ns[s[st]].next = s[ed+k];
	//每段子链表的非头一个节点常规处理 
	for(int i=ed;i>st;i--)
	{
		ns[s[i]].next = s[i-1];
	}
}
int main()
{
    //最后一个测试点的坑 :节点个数不等于节点长度,会出现独立于链表的结点
	int st,n;
	cin>>st>>n>>k;
	//存储节点 
	for(int i=0;i<n;i++)
	{
		int a,b,c;//地址,id,next 
		cin>>a>>b>>c;
		ns[a].id = b;
		ns[a].next = c;
	}
	int l = 0;//统计在链表中的节点数目 
	int t = st;
	while(t!=-1)
	{
		s[l] = t;
		t = ns[t].next;
		l++;
	}
	//翻转节点 
	lun = l/k;//需要翻转多少轮,不能用n/k,因为n个节点不一定都在链表中 
	int start = 0;
	for(int i=0;i<lun;i++)
	{
		invers(start,start+k-1,i);
		start += k;
	}
	//输出翻转后的节点 
	st = s[k-1];
	int i = st;
	while(i!=-1)
	{
		cout<<setw(5)<<setfill('0')<<i<<" "<<ns[i].id<<" ";
		if(ns[i].next!=-1)
			cout<<setw(5)<<setfill('0')<<ns[i].next<<endl;
		else
			cout<<-1<<endl;
		i = ns[i].next;
	}
}

测试点6:

题目给出了n个节点,但是这些节点不一定都在链表上,可能独立于链表之外,因此我们代码中的lun不能直接用n/k,而是先统计出来在链表上的节点数目l,再用l/k作为我们需要反转的子链表数目。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

owooooow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值