1025. 反转链表

来源:互联网 发布:网络远程教育可靠吗 编辑:程序博客网 时间:2024/06/05 07:31

一、题目

给定一个常数K以及一个单链表L,请编写程序将L中每K个结点反转。例如:给定L为1→2→3→4→5→6,K为3,则输出应该为3→2→1→6→5→4;如果K为4,则输出应该为4→3→2→1→5→6,即最后不到K个元素不反转。

输入格式:

每个输入包含1个测试用例。每个测试用例第1行给出第1个结点的地址、结点总个数正整数N(<= 105)、以及正整数K(<=N),即要求反转的子链结点的个数。结点的地址是5位非负整数,NULL地址用-1表示。

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

Address Data Next

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

输出格式:

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

输入样例:
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

二、个人理解

本题主要考察对数组的反转问题。个人思路为:定义结构体数组,进行逐一的反转。


关键点:

  • 如何得到顺序的节点连接。题目中给出的是无序,所以我们要进行转换,一开始我使用两个for循环,但是测试点运行超时,故后来改成用map(数组也可以)进行排序。
  • 节点的不断更新。我一开始是从前往后反转,最后的到18分,后来发现这样只有第一轮是正确的,当反转需要很多轮时,上一轮与下一轮经过反转后需进行连接处的更新,故我从后往前反转。
  • 无效节点的问题。当我得到24分时,我就知道肯定有个特殊的,果然测试点有开始地址不是头节点的时候,故我们需考虑这一点。

正常版(C++):

#include <iostream>#include <stdio.h>#include <map>using namespace std;typedef struct node {    int paddress;    int naddress;    int data;} linklist;int main(){    map<int, linklist> mp;    int first_address, num, k;    cin >> first_address >> num >> k;    linklist  sort_node[num], result[num], node;    int addr, data, next;    //赋值,并将其插入到map中,key 为地址,值为节点    for (int i = 0; i < num; i++) {        cin >> addr >> data >> next;        node.paddress = addr;        node.data = data;        node.naddress = next;        mp.insert(pair<int, linklist>(addr, node));    }    //遍历,得出节点的顺序连接,注意结束节点的下一地址为-1,且总长度可能不为num    int size = 0;    while (first_address != -1) {        sort_node[size] = mp[first_address];        first_address = sort_node[size].naddress;        size++;    }    //复制    for (int i = 0; i < size; i++) {        result[i] = sort_node[i];    }    //总共需几轮反转    int total = size / k;    //从后往前开始反转(不包括不能反转的)    while (total >= 1) {        int j = total * k - 1;        if (total < size / k) {            sort_node[j].naddress = result[j + 1].paddress;//完成一次反转后,进行这次反转的开头地址与即将开始的一轮反转的尾地址更新        }        first_address = sort_node[j].paddress;//取出一轮反转的首地址        for (int i = (total - 1) * k; i < k * total; i++) {            result[i].paddress = first_address;            result[i].data = sort_node[j].data;            if (j ==  (total - 1) * k) {                result[i].naddress = sort_node[total * k - 1].naddress;//当进行到反转最后一个时,后地址为原来结点的下地址                break;            }            result[i].naddress = sort_node[j - 1].paddress;            first_address = result[i].naddress;//记录节点的下一地址,为即将反转的节点服务            j--;        }        total--;    }    //输出,注意最后一个的输出    for (int i = 0; i < size - 1; i++) {        printf("%05d %d %05d\n", result[i].paddress, result[i].data, result[i].naddress);    }    printf("%05d %d %d\n", result[size - 1].paddress, result[size - 1].data, result[size - 1].naddress);}

关于这一个版本,我只能说我看问题的角度可能过于死板,非要把所有的东西绑在一起,那样太麻烦了。


下一版的主要算法为:对题目分析得出,其实每个节点的后地址就是下一地址的前地址,故我们只需要每个地址的前地址就ok了(而尾部地址一直为-1)。可以将前地址就当作每一节点的地址,然后创建一个数据数组,下标为前地址,值为后地址,另外一个地址数组,存放后地址。然后进行节点的连接,实际为地址的连接,并存放到list数组。直接对地址进行反转(实际就是对节点进行反转),这里就不需要考虑更新问题(因为其中根本没包括后地址)。最后输出时注意后地址就是下一节点的前地址。

超神版(转载):

#include <iostream>#include <algorithm>#include <stdio.h>using namespace std;int main(){    int first, k, n, temp;    cin >> first >> n >> k;    int data[100005], next[100005], list[100005];    for (int i = 0; i < n; i++) {        cin >> temp;        cin >> data[temp] >> next[temp];    }    int sum = 0;//不一定所有的输入的结点都是有用的,加个计数器    //list对应顺序的节点(以地址表示)    while (first != -1) {        list[sum++] = first;        first = next[first];    }    //直接对地址反转,本质上为对节点反转    for (int i = 0; i < (sum - sum % k); i += k) {        reverse(list + i * k, list + i * k + k);    }    //注意输出    for (int i = 0; i < sum - 1; i++) {        printf("%05d %d %05d\n", list[i], data[list[i]], list[i + 1]);    }    printf("%05d %d -1", list[sum - 1], data[list[sum - 1]]);    return 0;}
原创粉丝点击