原题: https://www.patest.cn/contests/pat-b-practise/1025
实现思路: 解决本题有2个关键点, 第1个是怎么把一堆打乱的链表节点, 按照头结点开始,
排好序. 第2个是给我们按顺序的一组数字, 再给个K, 我们怎么对这组数进行正确翻转.
也就是先解决:
输入
00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
输出
00100 1 12309
12309 2 33218
33218 3 00000
00000 4 99999
99999 5 68237
68237 6 -1
然后解决:
假设 k = 3
输入: 1 2 3 4 5 6 7 8 9
输出 3 2 1 6 5 4 8 9
第1个关键点, 可以采用开一个10万空间的数组, 让address
成为数组下标, 这样我们对数组按照
头结点顺序排序时, 时间复杂只为n, 遍历一遍即可完成排序.
第2个关键点, 比较容易, 在草稿纸上找找规律, 不难写出.
注意本题最后一个测试的坑: 输入可能存在"报废节点", 所以录入数据时我们要检查next
是否已经
等于-1, 不能认为给n个数据, 链表中就一定有n个数据.
版本1: 倒数第2个测试点运行超时, 无法AC
#include <stdio.h>
#include <stdlib.h>
struct node {
int address;
int data;
int next;
};
typedef struct node s_node;
int find (s_node nd[], int n, int addr);
void reverse (int source[], int dest[], int k, int n);
int main () {
int faddr;
int n;
int k;
s_node *nd;
s_node *temp;
int addr;
int i;
int j;
int x;
int pos; // 根据地址找下标, 保存在此
int *stnum; // 保存原来结构体的序号
int *renum; // 保存反转后的序号
int relen = 0; // 反转后的序号长度
scanf("%d %d %d", &faddr, &n, &k);
nd = (s_node*)malloc(sizeof(s_node) * (n + 1));
temp = (s_node*)malloc(sizeof(s_node) * (n + 1));
renum = (int*)malloc(sizeof(int) * (n + 1));
stnum = (int*)malloc(sizeof(int) * (n + 1));
for (i=1; i<=n; i++) {
scanf("%d %d %d", &temp[i].address, &temp[i].data, &temp[i].next);
}
// 按照顺序, 进行排序赋值
for (i=1; i<=n; i++) {
pos = find(temp, n, faddr);
nd[i] = temp[pos];
faddr = temp[pos].next;
if (faddr == -1) {
// 排除程序中的废点
n = i;
break;
}
}
free(temp);
for (i=1; i<=n; i++) {
stnum[i] = i;
}
// 反转序号
reverse(stnum, renum, k, n);
// 根据反转后的序号, 调整nd
for (i=1; i<=n-1; i++) {
j = renum[i];
x = renum[i + 1];
printf("%05d %d %05d
", nd[j].address, nd[j].data, nd[x].address);
}
// 最后一个数单独打印
j = renum[n];
printf("%05d %d %d
", nd[j].address, nd[j].data, -1);
return 0;
}
// 返回地址是addr的节点下标
int find (s_node nd[], int n, int addr) {
int i;
int pos = -1; // 等于-1说明没找到
for (i=1; i<=n; i++) {
if (nd[i].address == addr) {
pos = i;
break;
}
}
return pos;
}
void reverse (int source[], int dest[], int k, int n) {
int i; // i循环每次+k
int j; // j用来表示有k层内循环
int x; // 临时保存i的值
int len = 0;
for (i=k; i<=n; i+=k) {
x = i;
for (j=1; j<=k; j++) {
len++;
dest[len] = source[x];
x--;
}
}
// 最后不够的数, 加入末尾
if (i != n) {
for (j=i-(k-1); j<=n; j++) {
len++;
dest[len] = source[j];
}
}
}
版本2: 完整C语言实现 - 可以AC
参考: http://www.xuebuyuan.com/2078825.html
#include <stdio.h>
#include <stdlib.h>
struct node {
int address;
int data;
int next;
};
typedef struct node s_node;
void reverse (int source[], int dest[], int k, int n);
int main () {
int faddr; // 首地址
int n; // 节点总数
int k; // 反转单位
s_node *nd; // 从头节点开始排好序, 存在这里
s_node *temp; // 从屏幕读入数据存入这里
s_node tnode; // 临时节点
int *stnum; // 保存原来结构体的序号
int *renum; // 保存反转后的序号
int relen = 0; // 反转后的序号长度
int i;
int j;
int x;
scanf("%d %d %d", &faddr, &n, &k);
nd = (s_node*)malloc(sizeof(s_node) * 100010);
temp = (s_node*)malloc(sizeof(s_node) * 100010);
renum = (int*)malloc(sizeof(int) * (n + 1));
stnum = (int*)malloc(sizeof(int) * (n + 1));
for (i=1; i<=n; i++) {
scanf("%d %d %d", &tnode.address, &tnode.data, &tnode.next);
temp[tnode.address] = tnode; // 这步非常关键, 开10万个空间, 用途就在这
}
// 从头地址开始, 按顺序赋值
for (i=1; i<=n; i++) {
nd[i] = temp[faddr];
faddr = temp[faddr].next;
if (faddr == -1) {
// 排除报废节点
n = i;
break;
}
}
free(temp); // 用不到了
for (i=1; i<=n; i++) {
stnum[i] = i;
}
// 反转序号
reverse(stnum, renum, k, n);
// 根据反转后的序号, 调整nd, 也就是调整打印顺序
for (i=1; i<=n-1; i++) {
j = renum[i];
x = renum[i + 1];
printf("%05d %d %05d
", nd[j].address, nd[j].data, nd[x].address);
}
// 最后一个数单独打印
j = renum[n];
printf("%05d %d %d
", nd[j].address, nd[j].data, -1);
return 0;
}
void reverse (int source[], int dest[], int k, int n) {
int i; // i循环每次+k
int j; // j用来表示有k层内循环
int x; // 临时保存i的值
int len = 0;
for (i=k; i<=n; i+=k) {
x = i;
for (j=1; j<=k; j++) {
len++;
dest[len] = source[x];
x--;
}
}
// 最后不够的数, 加入末尾
if (i != n) {
for (j=i-(k-1); j<=n; j++) {
len++;
dest[len] = source[j];
}
}
}