问题描述:
给定一个常数 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
问题分析:
首先,根据题目的设定,我们要考虑创建一个链表节点的结构体进行存储链表节点的数据,一整个链表就用这个结构体数组进行存储即可。由于录入的数据是一个链表,所以我们在录入的过程中有些技巧,不能将录入的顺序号作为结构体数组的下标,而应该直接用地址作为结构体的下标,这样便于进行排序。
这题在解题上有2种思路,一种是数组思路,一种是链表思路。如果用数组的方式去求解这题,需要额外再创建一个节点的数组指针,先把所有的节点按照顺序排好,然后再翻转。
个人理解,还是用数组的方式处理这个问题更加的高效。实现的方式直接见代码就可以了,非常的清晰明了。
关于链表法的总体思路是,先把数据读进来,然后写一个针对链表翻转的函数,函数功能如下:
第一个节点的next指针开始时不动,第一个节点往后的next指针指向前一个节点(这里有点绕),一直轮询K次(即开始时输入的K的值),此时已得到这段需要翻转的链表的最后一个节点的next,先存下next的参数,然后把next指向前一个节点,此时,再将第一个节点的next值改为最后一个节点的next值,此时这一段链表已完成整个翻转过程。链表的方式处理这个问题还是有点繁琐的。
这题的测试点有一个坑:链表的节点数不一定是N个。题目并没有说这N个节点都是属于链表的,言外之意就是,可能有几个节点不在这个链表上,需要考虑这个问题。
最后说一下,在考试时候,肯定是考虑使用数组的方式进行求解,这种方式的代码量相对较少,而且思路非常的清晰,很快可以把问题进行处理。如果想把链表这个数据结构学习的扎实一点,不妨用链表改指针的方式把这道题给求解出来,不过链表法用起来思路比较的乱,因为指针比较的多,中间的temp变量也多,容易把思路弄混乱,但是可以训练解决数据结构中链表问题的能力。
数组方式求解的代码:
#pragma warning(disable:4996)
#include <stdio.h>
#include <algorithm>
using namespace std;
struct node
{
int add;
int data;
int next;//新的链表下一个节点的地址
};
node List[100010];
node* Plist[100010];//定义一个指针数组,指向List
int main()
{
int first, step=0;
int N, K;
scanf("%d%d%d", &first, &N, &K);
int add, data, next;
for (int i = 0; i < N; i++)
{
scanf("%d%d%d", &add, &data, &next);
List[add].add = add;
List[add].data = data;
List[add].next = next;
}
while (first !=-1)
{
Plist[step] = &List[first];
first = List[first].next;
step++;//节点的数量为step最后的值
}
for (int i = 0; i < (step - step % K); i += K)//数组指针进行倒序排列
reverse(Plist + i, Plist + i + K);
for (int i = 0; i < step-1; i++)//输出
printf("%05d %d %05d\n", Plist[i]->add, Plist[i]->data, Plist[i+1]->add);
printf("%05d %d -1", Plist[step-1]->add, Plist[step-1]->data);//输出最后一个参数
return 0;
}
链表方式求解的代码:
#pragma warning(disable:4996)
#include <string.h>
#include <stdio.h>
using namespace std;
struct node
{
int data;
int nextOld;//原先链表下一个节点的地址
int next;//新的链表下一个节点的地址
};
node List[100010];
/*
备注:这里所说的指针并不是指针类型,而是指向数组的下标。指针不一定非要用指针类型进行表示。
*/
int reverse(node list[], int k, int first)//k为节点数,p为一段链表中首地址,返回翻转后最后一个地址
{
int add, next;
int pointer;//指向当前位置的指针
//首节点操作与其他节点操作有所不同
add = first;//add保存指针pointer所指的前一个地址
pointer = list[first].next;
for (int i = 1; i < k; i++)//从第二个节点进行操作
{
next = list[pointer].next;//将链表当前节点的下一个地址进行保存在temp2
list[pointer].next = add;//链表指针指向前一个地址
add = pointer;//地址改为当前指针所指的节点
pointer = next;//指针指向下一个节点
}
list[first].next = pointer;//首节点指向下一个
return add;//返回当前段链表最后的一个地址
}
int main()
{
int first, temp, step=0;
int temp2,temp3;
int N, K;
scanf("%d%d%d", &first, &N, &K);
int add, data, next;
for (int i = 0; i < N; i++)
{
scanf("%d%d%d", &add, &data, &next);
List[add].data = data;
List[add].nextOld = next;
List[add].next = next;
}
temp = first;
while (temp!=-1)
{
temp = List[temp].next;
step++;
}
temp = first;//存上一个链表原先第一个节点
first = reverse(List, K, temp);//头节点的地址变成第一个链表中最后一个节点的地址
temp2 = first;
for (int i = 2 * K; i <= step; i += K)
{
temp2 = List[temp2].nextOld;//往后移一位
temp3 = temp2;
temp2 = reverse(List, K, temp2);
List[temp].next = temp2;
temp = temp3;
}
while (List[first].next!=-1)
{
printf("%05d %d %05d\n", first, List[first].data, List[first].next);
first = List[first].next;
}
printf("%05d %d -1", first, List[first].data);
return 0;
}