链表处理

2 篇文章 0 订阅
1 篇文章 0 订阅

题目描述

给定一个常数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

思路

  1. 定义静态链表。其中结点性质由int型变量order定义,表示结点在链表中的序号(从0开始),其中无效结点为maxn。
  2. 初始化。令order的初值均为maxn,表示初始时所有结点都为无效结点。
  3. 由题目给出的链表首地址begin遍历整条链表,并记录每个有效结点在链表中的序号(即给order赋值),同时计数有效结点的个数count。之后为了后面的步骤方便书写,这里应把count赋给n。
  4. 对结点进行排序,排序函数cmp的排序原则是:直接按照结点的order从小到大排序。由于有效结点的order从0开始,无效结点的order均为maxn,因此排序后前面都是有效结点。
  5. 输出链表,不过这题的题目条件比较繁琐。由于需要每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号结点,即尾巴的第一个结点:接下来,从前往后输出尾巴的所有结点。

注意点

  1. 要考虑可能存在无效结点的情况,即不是题目给出的头结点引出的单链表上的结点,这些结点是要去掉的,最终不予输出。
  2. 反转链表只改变结点的next地址,而不会改变本身的地址,因此address和data可以视为绑定的。
  3. %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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值