前言
鸽了好久好久又回来发日记了/忏悔
这次的题同样是鸽了好久没做出来,是浙大数据结构(陈越、何钦铭)里的一道习题,同时也是PAT甲级的一道题目
上题
#include <iostream>
#define MAXSIZE 100000
using namespace std;
typedef struct node *ptr;
struct node {
string address; // 保存多个0
int data;
string next;
ptr Next;
} array[MAXSIZE];
ptr Reverse( ptr Head, int K ) { // Head为当前要逆转的链表的头结点的前一个结点
ptr New = Head->Next; // 当前逆转好的新链表的头结点
ptr Old = New->Next; // 当前还没有完成逆转的老链表的头结点
ptr tmp; // Old的下一个
for( int cnt = 1; cnt < K; cnt++ ) {
tmp = Old->Next;
Old->Next = New;
New = Old;
Old = tmp;
}
tmp = Head->Next;
tmp->Next = Old;
Head->Next = New;
return tmp; // 将逆转好的链表的尾结点返回, 作为下次逆转的Head
}
int main() {
string ads;
int firstAdd;
int add, N, K, count; // 注意N只是结点数, 可能有多余结点
int num = 0; // 有效结点数(不含空头结点)
ptr Head = new node; // 空头结点
ptr t = Head;
cin >> firstAdd >> N >> K;
// 输入结构体数组
for( int i = 0; i < N; i++ ) {
cin >> ads; // string的ads为文本地址
add = stoi(ads); // 字符串转int, add为数组下标, 即数组中的“地址”
array[add].address = ads;
cin >> array[add].data >> array[add].next;
}
// 构建链表
if( array[firstAdd].data ) {
Head->Next = t = &array[firstAdd];
num++;
}
for( ; t->next != "-1"; t = t->Next ) {
num++;
add = stoi( t->next );
t->Next = &array[add];
}
// 反转链表
if( K != 1 ) { // K为1不用反转
ptr HeadCopy = Head;
if( K > num )
Reverse( HeadCopy, num );
else {
count = num / K; // 反转次数
for( int i = 0; i < count; i++ ) {
HeadCopy = Reverse( HeadCopy, K );
}
}
}
// 输出结果
for( t = Head->Next; t; t = t->Next ) {
if( t != Head->Next ) cout << endl;
cout << t->address << " " << t->data << " ";
if( t->Next ) cout << t->Next->address;
else cout << "-1";
}
}
做题经历
个人做这道题时卡了非常之久,一开始按自己的想法做一直过不了,心态稍有点崩,后来由于各种原因这道题就搁置下了。今天听了陈越老师的习题讲解课,按照她提供的基本思路去敲,最初仍是只能通过两个测试点(悲),这题需要注意的细节之处真的非常多。经过几个小时终于全通过。
说几点重要的吧
1.对地址的处理
首先我是用链表去实现的,题目中的 address 和 next 都是 虚拟地址,真正将各个结点串起来的是结构体node里的 Next指针,但是题目要求输出 address 和 next ,所以不能只处理指针的值,必须将 next 看作是真的下一结点的地址,因为其值在反转链表后必然会改变。
但是一次反转后的子链的尾结点的下一结点是不确定的,因为后面的反转还没有完成。
这里可以在反转后直接将每一结点的next值改为下一结点的address,因为data和address对一个结点来说是固定不变的。
但我这里偷懒了,直接在输出next的时候输出下一结点的address,没有则为-1
2.降低时间复杂度(构建链表)
一开始我按照从0到N的顺序将每一个结点存入那个大小为100000的结构体数组,构建链表的时候就需要遍历一遍数组去寻找与当前结点的next值相同的address,从而让Next指针值指向这个“下一个结点”
这样的时间复杂度应该是O(N^2),过不了第五个测试点(超时)
如何优化呢?
开了这么大的数组当然要充分利用!
数组大小是100000的原因就是最多会有100000个结点,而结点“地址”又刚好是5位(00000~99999),那么每一个结点的地址就可以对应一个数组下标
但是注意还是要用string来保存有多个前缀0的“地址”,不然00000就是0,那么只需要用stoi()来将string转换为int就可以了
这样构建链表的时间复杂度就降为O(N)
3.有效结点数
为了卡一些“排序算法”,测试用例会有一些多余结点,所以要额外计算有效结点数num
另外就是一些边界问题,反转次数之类的,还有别的话再补充...
一点话说
为什么这么难啊!
为什么我这么能鸽啊!!
哭!!!