题目:
给定一个常数 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作为我们需要反转的子链表数目。