题目
给定一个常数 K 和一个单链表 L,请你在单链表上每 K 个元素做一次反转,并输出反转完成后的链表。如果链表最后一部分不足 K 个元素,则最后一部分不翻转。
例如,假设 L为 1→2→3→4→5→6,如果 K=3,则你应该输出 3→2→1→6→5→4;如果 K=4,则你应该输出 4→3→2→1→5→6
补充
1、本题中可能包含不在链表中的节点,这些节点无需考虑。
输入格式
第一行包含头节点地址,总节点数量 N以及常数 K。
节点地址用一个 5 位非负整数表示(可能有前导 0),NULL 用 −1表示。
接下来 N 行,每行描述一个节点的信息,格式如下:
Address Data Next
其中 Address 是节点地址,Data 是一个整数,Next 是下一个节点的地址。
输出格式
将重新排好序的链表,从头节点点开始,依次输出每个节点的信息,格式与输入相同。
数据范围
1≤N≤105,
1≤K≤N
输入样例:
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
代码以及思路
就爱做水题~
思路
数组模拟链表
题目明示了用数组来模拟链表了,那我们就这么干吧。
创建一个如下的结构体,同时创建一个长度为100001(或更大)的数组。名字随你便起:
struct Link
{
int data, next;
} L[100001];
为什么要多一个呢?注意到这题大概率要对第一个节点进行操作,所以我们再多分配一个首节点来方便后续的操作。题目有说明节点的地址要在0~99999之间,因此这里我把head节点分配在100000,这将在接下来的代码体现。
继续反转的条件判断
如题的:
如果链表最后一部分不足 K 个元素,则最后一部分不翻转。
,我们就要判断链表剩下的元素是否有 K 个。考虑到后续在实现对一组 K 个元素反转后我们要获得接下去的节点地址,因此我们写一个函数,用来读取距离当前节点 K 个位置后的节点吗,我将它命名为k_after
函数如下:
int k_after(int address, int k) {
for (int i = 0; i < k; i++) {
// 当在向后探索的过程中如果到达的链尾,那么我们直接结束循环就行
// 这样在外部处理时,如果返回的值为-1,那么就可以直接停止处理了
if (address == -1) break;
address = L[address].next;
}
return address;
}
处理反转
我们先想这么在单组 K 个节点之间处理,之后用一个循环就能直接结束这道题了,在这里我用一个handle函数来处理这个问题,那么handle要怎么写呢?
要对这 K 个节点反转,我们需要修改的指针就应该是 K+1 个,其中,位于内部的 K-2 个节点的指针特别容易修改,只需要我们记录节点的顺序再依次反转即可,如:
order[0] = L[previous_node].next;
for (int i = 1; i < k; i++)
order[i] = L[order[i - 1]].next;
for (int i = k - 1; i >= 1; i--) {
L[order[i]].next = order[i - 1];
}
这里的previous_node是这 K 个节点头的前一个节点,因为我们需要修改这个节点的next指针的值。
剩下的两个 指向这 K 个序列头的指针 以及 这 K 个序列尾指向的指针 需要我们进行单独的操作。
指向这 K 个序列头的指针
处理完的序列头一定是原先的最后一个节点,所以我们直接把previous_node的next赋值为order数组的最后一个元素就行
这 K 个序列尾指向的指针
这个指针指向原本是由最后一个元素记录的,但是我们发现再反转内部节点时把这个指向丢失了,于是我们要对上面的代码添加一个变量用来存储原本指向的地址,并在最后把新的结尾的指针指向它。综上,函数长这样:
void handle(int previous_node, int k) {
int order[k], out;
order[0] = L[previous_node].next;
for (int i = 1; i < k; i++)
order[i] = L[order[i - 1]].next;
out = L[order[k - 1]].next; // 记录
for (int i = k - 1; i >= 1; i--) {
L[order[i]].next = order[i - 1];
}
L[order[0]].next = out; // 新的尾
L[previous_node].next = order[k - 1]; // 新的头
}
到这里就差不多写完了。
代码
// https://www.acwing.com/problem/content/1562/
#include<bits/stdc++.h>
using namespace std;
struct Link
{
int data, next;
} L[100001];
int k_after(int address, int k) {
for (int i = 0; i < k; i++) {
if (address == -1) break;
address = L[address].next;
}
return address;
}
// 输入要交换的前一个节点, 以及反转长度.
void handle(int previous_node, int k) {
int order[k], out;
order[0] = L[previous_node].next;
for (int i = 1; i < k; i++)
order[i] = L[order[i - 1]].next;
out = L[order[k - 1]].next;
for (int i = k - 1; i >= 1; i--) {
L[order[i]].next = order[i - 1];
}
L[order[0]].next = out;
L[previous_node].next = order[k - 1];
}
void print(int node) {
int next_node = L[node].next;
if (next_node == -1) return;
if ( L[next_node].next == -1) printf("%05d %d -1\n", next_node, L[next_node].data);
else printf("%05d %d %05d\n", next_node, L[next_node].data, L[next_node].next);
print(next_node);
}
int main(){
int num, k, temp = 100000;
cin >> L[100000].next >> num >> k;
for (int i = 0;i < num; i++) {
int address;
cin >> address;
cin >> L[address].data >> L[address].next;
}
while (k_after(temp, k) != -1) {
handle(temp, k);
temp = k_after(temp, k);
}
print(100000);
}