这个题我看了好多人的实现,发现他们是用了两个MAXSIZE大小的数组:
第一个数组是保存输入
第二个数组是保存的这些输入的顺序(将输入按照题意排序保存于这个数组)
这样做很简单,但是看看这个想法的 空间复杂度O(2*MAXSIZE)
我也是个菜鸡,可能分析有误,希望大家指出
而我算法的空间复杂度是O(MAXSIZE+K),而时间复杂度只是稍稍高于以上思路
算法思路:
我以一个数组保存题中的输入,为了方便,数组大小就是MAXSIZE,这样任何一个题目中元素的地址就是就是在这个数组中的下标,然后我开始从题中规定的顺序开始从1记数,题中也要求每K个倒序输出一下,所以每当记得数为K的倍数,我就倒序输出这K个数
难点
倒序输出很简单,我代码里在Prin函数t中,但是有一个盲点(华点,我发现了盲生):假设(N=5)有5个数,(K=2)每2个倒序输出一下(我的代码里是用了个长度为K的数组保存这K个数,然后把他们倒序输出),所以要倒序输出两轮,
例如: 1 2 3 4 5 —》 2 1 4 3 5
很好,这就是我的思路,然鹅。。。。
这个结点保存的Next是3,但是在第一轮倒序后,使得1的Next保存的是3
到了第2次倒序,我们把3和4的位置颠倒了
那么问题来了:第一轮里1保存的Next是3的,但在第二轮里面4成了倒序后的第一位,即1的Next需要保存4,而不是3。。。
解决办法:我们知道每K个数就要倒置一个,所以(呵呵),如果我这一轮进行了倒置,那么我倒置后的最后一个元素的Next有两种结果:
- 当这一轮倒置后,下面的数不够K个数(也即之后的数不用倒序了),那么倒置后最后一个Next就是这一轮元素的下一个元素
例如: 1 2 3 —》 2 1 3
倒置后的元素1 2倒置成了2 1,然后1的Next是这一轮元素的下一个元素 3
- 当这一轮的倒置结束后,接下来的数比K个多,即一定会有下一轮的倒序,此时这一轮倒置后最后一个元素的Next要指向距离他K个元素的地址
例如: 1 2 3 4 5—》 2 1 4 3 5
第一轮倒序结束后 2 1,而此时1的Next需要保存的是距离他K个元素的4
这个解决办法在代码中的Print函数中的while循环里
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100005
int TheFirst, N, K, printNUM = 0;//printNUM为已近输出的个数,为了找到最后一个输出的元素(其Next为-1)来特殊处理
struct Node {
int address;//保存这个元素的地址,以便在反转的时候可以方便让别的元素指向它
int data;
int Next;
}Node[MAXSIZE];
void Init() {
int address;
scanf("%d %d %d", &TheFirst, &N, &K);
int n = N;
while (n--) {
scanf("%d", &address);
Node[address].address = address;
scanf("%d", &Node[address].data);
scanf("%d", &Node[address].Next);
}
}
void Print(struct Node* Reserve) {//每K个反转一次 注意,此时的反转后最后一个要指向下一轮的第一个(可能下一轮也要反转,所以此轮最后一个要指向下一轮反转后的第一个)
//此时在改变颠倒后的Next域
int address = Reserve[K - 1].Next; //保存最后一个要Next,以便倒置结束后赋给倒置后第一个值的Next
for (int i = K - 1;i >= 1;i--) {
Reserve[i].Next = Reserve[i - 1].address;
}
int k = 0, ad = address;
while (ad != -1) {//注意这一步是最关键的: 第一轮倒置的结果后上面进行了赋值,但是 下一轮如果还要倒置的话,需要将这一轮倒置后的最后一个Next重新给下一轮倒置后的第一个值
//找下一轮倒序后的的第一个值
if (++k == K) {
address = Node[ad].address;//下一轮如果倒置,那么他的第一个值就是与我们这一轮未倒置时最后一个元素相差K,找到他的值,将他的值给我们这一轮倒置后的最后一个值(即未倒置时第一个元素)的Next
break;
}
ad = Node[ad].Next;
}
Reserve[0].Next = address;
for (int i = K - 1;i >= 0;i--) {
printNUM++;
if (printNUM != N) {
printf("%05d %d %05d\n", Reserve[i].address, Reserve[i].data, Reserve[i].Next);
}
else {
printf("%05d %d %d\n", Reserve[i].address, Reserve[i].data, Reserve[i].Next);
}
}
}
int main() {
Init();
struct Node* Reserve = (struct Node*)malloc(sizeof(struct Node) * K);//存放要反转的K个数 其实反转是在Reserve中,对原来的数组Node没影响
int address = TheFirst;
int sum = 0;
while (address != -1) {
sum++;
if (sum % K == 0) {
Reserve[K - 1].address = Node[address].address; Reserve[K - 1].data = Node[address].data; Reserve[K - 1].Next = Node[address].Next;
Print(Reserve);
}
else {
Reserve[sum % K - 1].address = Node[address].address; Reserve[sum % K - 1].data = Node[address].data; Reserve[sum % K - 1].Next = Node[address].Next;
}
address = Node[address].Next;
}
if (sum % K != 0) {
for (int i = 0;i < sum % K - 1;i++) {
printf("%05d %d %05d\n", Reserve[i].address, Reserve[i].data, Reserve[i].Next);
}
printf("%05d %d %d\n", Reserve[sum % K - 1].address, Reserve[sum % K - 1].data, Reserve[sum % K - 1].Next);//对最后一个单独输出 因为最后一个地址为-1不用补0
}
return 0;
}
这个代码过了不到几天我又看了下,一下子感到了害怕,无助;简直不敢相信这是我写的代码,当时真的不知道是什么让我把这个写出来。。。
如果大家有更好的思路过程的话请在评论里留言,希望我们可以共同进步,不断提高自己的算法能力