PAT 乙级练习 1025 反转链表

PAT 乙级练习 题解合集

本题链接

题目

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

思路

写在前面的废话
第一次做这种给出地址的链表题很不习惯,看完题目,链表?反转?这不刚学完数据结构吗哈哈哈,头插法搞起来…于是写完反转函数一跑,指针都飞到天上去了…硬着头皮把 bug 都修完了去看了柳神的思路,顿时感觉自己把路走窄了。于是领会了一下核心思想,自己又重写了一版,虽然做不到那么精炼,但还是写一下自己的步骤吧。


步骤:

  1. 开一个长度100010的大数组nodes接收所有结点,用head记录头结点地址;
  2. head开始遍历一遍链表,把结点的地址按照顺序存放在数组linklist里;
  3. 在数组linklist里按照题意把整个链表反转好;
  4. 根据linklist里此时的顺序输出结点(注意这个时候不需要做额外的事情了,第i个结点的地址就是linklist[i],数据是nodes[linklist[i]].data,下一个结点的地址是linklist[i+1]

坑点:

  • 不是所有给的结点都在链表里,可能存在没有用的结点;
  • 所有地址,除了最后的-1,都有前导 0。

代码

在数组里反转
#include <stdio.h>

#define MAX 100010

typedef struct {
	int data, next;
} Node; 

Node nodes[MAX];	// 接收输入 
int linklist[MAX]; 	// 存放所有链表结点的地址

void reverse(int *a, int len) {
	int i;
	for (i = 0; i < len / 2; ++i) {
		int temp = a[i];
		a[i] = a[len - i - 1];
		a[len - i - 1] = temp;
	}
} 

int main() {
	int head, n, k, i, add;
	scanf("%d %d %d", &head, &n, &k);
	for (i = 0; i < n; ++i) {
		scanf("%d", &add);
		scanf("%d %d", &nodes[add].data, &nodes[add].next);
	}
	int p = head, cnt = 0;
	while (p != -1) {
		linklist[cnt++] = p;
		p = nodes[p].next;
	}
	int done = 0;
	while (done + k <= cnt) {
		reverse(linklist + done, k);
		done += k;
	}
	for (i = 0; i < cnt; ++i) {
		printf("%05d %d ", linklist[i], nodes[linklist[i]].data);
		if (i == cnt - 1)
			printf("-1\n");
		else
			printf("%05d\n", linklist[i + 1]);
	} 
	return 0;
} 
头插法(最早写的,感兴趣可以看看)
#include <stdio.h>

#define MAX 100010

typedef struct {
	int data, next;
} Node; 

Node nodes[MAX];

int count(int head) {
	int cnt = 0, p = head;
	while (p != -1) {
		++cnt;
		p = nodes[p].next;
	} 
	return cnt;
} 

// 修改 head 的后 1 个结点到 head 的后 len 个结点的 next
// 并且返回 head 的后 1 个结点的地址 
// 例如对于 1 →2 →3 →4 →5 →6,reverse( 2 的地址, 3)后
// 链表变成 1 →2 →5 →4 →3 →6,并返回 3 的地址
int reverse(int head, int len) {
	int p = nodes[head].next, q, i;
	int ret = p;
	nodes[head].next = -1;
	for (i = 0; i < len; ++i) {
		q = nodes[p].next;
		nodes[p].next = nodes[head].next;
		nodes[head].next = p;
		p = q;
	}
	nodes[ret].next = p;
	return ret;
} 

void print(int head) {
	int p = head;
	while (p != -1) {
		printf("%05d %d ", p, nodes[p].data);
		if (nodes[p].next == -1)
			printf("-1\n");
		else
			printf("%05d\n", nodes[p].next);
		p = nodes[p].next;
	}
} 

int main() {
	int head, n, k, i, add;
	scanf("%d %d %d", &head, &n, &k);
	for (i = 0; i < n; ++i) {
		scanf("%d", &add);
		scanf("%d %d", &nodes[add].data, &nodes[add].next);
	}
	int cnt = count(head);	// 统计总结点个数
	int done = 0;			// 记录已经处理好的节点个数
	nodes[100000] = {0, head};	// 造一个真正的头结点
	int p = 100000;				// 用 p 遍历链表 
	while (done + k <= cnt) {
		p = reverse(p, k);
		done += k;
	}
	print(nodes[100000].next);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值