题目描述
给定一个常数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),其中k即为要求反转的子链结点的个数。结点的地址是5位非负整数,NULL地址用-1表示。
接下来有N行,每行格式为:
Address Data Next
其中Address是结点地址,Data是该结点保存的整数数据,Next是下一结点的地址。
输出格式
对于每个测试用例,顺序输出反转后的链表,其上每个结点占1行,格式与输出相同。
输入格式
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
思路
- 定义静态链表。其中结点性质由int型变量order定义,表示结点在链表中的序号(从0开始),其中无效结点为maxn。
- 初始化。令order的初值均为maxn,表示初始时所有结点都为无效结点。
- 由题目给出的链表首地址begin遍历整条链表,并记录每个有效结点在链表中的序号(即给order赋值),同时计数有效结点的个数count。之后为了后面的步骤方便书写,这里应把count赋给n。
- 对结点进行排序,排序函数cmp的排序原则是:直接按照结点的order从小到大排序。由于有效结点的order从0开始,无效结点的order均为maxn,因此排序后前面都是有效结点。
- 输出链表,不过这题的题目条件比较繁琐。由于需要每k个结点反转1次,因此可以把n个结点分为n/k个完整的块,这时如果n%k不为0,那么后面会剩“一点尾巴”(不完整的块)。
枚举这些完整的块,对每一个块,从后往前输出结点信息。唯一要注意的就是每一个块的最后一个结点的next的处理:
设当前处理的是i号完整块的最后一个结点。
1、如果i号块不是最后一个完整块,那么next就是(i+2)*k-1号结点,也就是(i+1)号块的最后一个结点。
2、如果i号块是最后一个完整块,同样分为两种情况:
如果n%k为0,说明这是整个单链表的最后一个结点,输出-1.
如果n%k不为0,说明在这个完整块后面还有“一点尾巴”。首先,这个完整块的最后一个结点的next是(i+1)k号结点,即尾巴的第一个结点:接下来,从前往后输出尾巴的所有结点。
注意点
- 要考虑可能存在无效结点的情况,即不是题目给出的头结点引出的单链表上的结点,这些结点是要去掉的,最终不予输出。
- 反转链表只改变结点的next地址,而不会改变本身的地址,因此address和data可以视为绑定的。
- %05d的输出格式会使-1的输出出现问题,因此一定要将-1的输出跟其他地址的输出分开来考虑。
参考代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100010;
struct Node
{//定义静态链表
int address, data, next;
int order;//结点在链表上的序号,无效结点记为maxn
}node[maxn];
bool cmp(Node a, Node b)
{
return a.order < b.order;//按order从小到大顺序
}
int main()
{
for (int i = 0; i < maxn; i++)
{//初始化
node[i].order = maxn;//初始化全部为无效结点
}
int begin, n, k, address;
scanf_s("%d%d%d", &begin, &n, &k);//起始地址,结点个数,步长
for (int i = 0; i < n; i++)
{
scanf_s("%d", &address);
scanf_s("%d%d", &node[address].data, &node[address].next);
node[address].address = address;
}
int p = begin, count = 0;//count 计数有效结点的数目
while (p != -1)//遍历链表找出单链表的所有有效结点
{
node[p].order = count++;//结点在单链表中的序号
p = node[p].next;//下一个结点
}
sort(node, node + maxn, cmp);//按单链表从头到尾顺序排列
//有效结点为前count个结点,为了下面书写方便,因此把count赋给n
n = count;
//单链表已经形成,下面是按题目要求输出
for (int i = 0; i < n / k; i++)
{
for (int j = (i + 1) * k - 1; j > i * k; j--)
{//第i块倒着输出
printf("%05d %d %05d\n", node[j].address, node[j].data, node[j - 1].address);
}
//下面是每一块的最后一个结点的next地址的处理
printf("%05d\n", node[(i + 2) * k - 1].address);
if (i < n / k - 1)
{//如果不是最后一块,就指向下一块的最后一个结点
printf("%05d\n", node[(i + 2) * k - 1].address);
}
else
{//是最后一块时
if (n % k == 0)
{//恰好是最后一个结点,输出-1
printf("-1\n");
}
else
{//剩下不完整的块按原先的顺序输出
printf("%05d\n",node[(i + 1) * k ].address);
for (int i = n / k * k; i < n; i++)
{
printf("%05d %d", node[i].address, node[i].data);
if (i < n - 1)
{
printf("%05d\n", node[i + 1].address);
}
else
{
printf("-1\n");
}
}
}
}
}
return 0;
}