区块反转

122 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情

题目描述

给定一个单链表 L,我们将每 K个结点看成一个区块(链表最后若不足 K 个结点,也看成一个区块),请编写程序将 L 中所有区块的链接反转。

例如:给定 L 为 1→2→3→4→5→6→7→81→2→3→4→5→6→7→8,K 为 33,则输出应该为 7→8→4→5→6→1→2→37→8→4→5→6→1→2→3。

补充 本题中可能包含不在单链表中的节点,这些节点无需考虑。

输入格式

第 1 行给出第 1 个结点的地址、结点总个数正整数 N、以及正整数 K,即区块的大小。结点的地址是 5 位非负整数(可能包含前导 0),NULL地址用 −1 表示。

接下来有 N 行,每行格式为:

Address Data Next

其中 Address 是结点地址,Data 是该结点保存的整数数据,Next 是下一结点的地址。

输出格式

对每个测试用例,顺序输出反转后的链表,其上每个结点占一行,格式与输入相同。

数据范围

1≤K≤N≤105

输入样例:

00100 8 3
71120 7 88666
00000 4 99999
00100 1 12309
68237 6 71120
33218 3 00000
99999 5 68237
88666 8 -1
12309 2 33218

输出样例:

71120 7 88666
88666 8 00000
00000 4 99999
99999 5 68237
68237 6 00100
00100 1 12309
12309 2 33218
33218 3 -1

思路

(模拟) O(n)O(n) 按题意模拟即可。 注意事项

  1. 并不是所有的结点都在 L 上
  2. 最后一个区块的边界问题
  3. 最后一个节点要输出-1

时间复杂度 很明显,我们在反转区块的时候每个结点只遍历了 1 次,所以时间复杂度是 O(n)。

空间复杂度 程序中开的链表长度最多为 n,所以空间复杂度也是 O(n)。

代码

#include <iostream>using namespace std;
​
const int N = 100010;
​
int k, n, h; // h表示头结点地址
int e[N], ne[N], l[N], len_l, res[N], len_r; // e存结点保存的整数数据,ne存下一结点的地址,l存链表,res存结果,len_l存l的长度,len_r存res的长度int main()
{
    cin >> h >> n >> k;
    while (n -- )
    {
        int addr;
        cin >> addr >> e[addr] >> ne[addr]; // 读入结点
    }
​
    int i = h;
    do // 初始化l
    {
        l[len_l ++ ] = i;
        i = ne[i];
    }
    while (~i);
​
    len_r = len_l; // 要倒序
    for (int i = 0; i < len_l; i += k) // i表示区块开头
        for (int j = min(len_l, i + k) - 1; j >= i; j -- ) // 从区块结尾开始枚举
            res[ -- len_r] = l[j]; // 存到结果数组里
​
    for (int i = 0; i < len_l; i ++ ) // 遍历
    {
        printf("%05d %d ", res[i], e[res[i]]);
        if (i ^ len_l - 1) printf("%05d\n", res[i + 1]); // 如果这个结点不是最后一个就输出下一个结点的地址
        else puts("-1"); // 否则输出-1
    }
​
    return 0;
}
​