1025. 反转链表 (25)

原题: 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\n", nd[j].address, nd[j].data, nd[x].address);
    }
    // 最后一个数单独打印
    j = renum[n];
    printf("%05d %d %d\n", 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\n", nd[j].address, nd[j].data, nd[x].address);
    }
    // 最后一个数单独打印
    j = renum[n];
    printf("%05d %d %d\n", 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];
        }
    }
}

1025.jpg

posted @ 2017-10-22 20:18  阿胜4K  阅读(254)  评论(0编辑  收藏  举报