题目详情:
Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K=3, then you must output 3→2→1→6→5→4; if K=4, you must output 4→3→2→1→5→6.
Input Specification:
Each input file contains one test case. For each case, the first line contains the address of the first node, a positive N (≤105) which is the total number of nodes, and a positive K (≤N) which is the length of the sublist to be reversed. The address of a node is a 5-digit nonnegative integer, and NULL is represented by -1.
Then N lines follow, each describes a node in the format:
Address Data Next
where Address
is the position of the node, Data
is an integer, and Next
is the position of the next node.
Output Specification:
For each case, output the resulting ordered linked list. Each node occupies a line, and is printed in the same format as in the input.
Sample Input:
00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
Sample Output:
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1
简单翻译:
本题就是输入了一个乱序的链表,只是告诉你每个节点的地址,数据域与下一个节点的地址,要先顺出一个完整链表,然后还要倒置指定长度,如果剩下长度不足指定长度就不倒置
主要思路:
首先要有一个结构体数组用来存储原始数据的数据域和下一个节点地址,当前节点的地址用另一个数据结构存储,可以是链表也可以是数组
如果用链表,则第一个存储原始数据的结构体数组的下标就是原始数据的节点地址,通过遍历下标(利用了结构体里存储的下一个节点地址)实现创建链表
为了实现倒置链表,这个创建的链表是双向链表,这样倒置时利用双指针,外层循环条件是现在移动位置加上移动距离小于链表长度,先将后指针移动到子序列末尾,然后将前后指针所指的数据域(里面是当前节点“地址”(不是真节点地址))交换,然后前指针后移,后指针前移,两者相交(相等或前跑到后的后面,这里可以用最近学的离散命题知识解释,记p:相等, q:到后面,¬(p∨q) 等价于¬p ∧¬q)时结束内层循环
完成倒置后打印输出,遍历链表,从中取出节点”地址“,用这个”地址“访问结构体数组取出里面存放的数据域,打印下一个节点”地址“则是取链表下一位,要判断链表下一位是不是空,如果是空就打印-1且不带换行
如果用数组,思路基本一致
第一次写错误:
题干将N是节点数量,不是链表长度,可能有不在链表上节点
代码实现:
两个数组版:
#include <stdio.h>
#include <stdlib.h>
#define maxsize 100002
struct LNode {
int Data;
int Next;
} a[maxsize]; //a这个数组是用来存储数据
int list[maxsize]; //每个结点的地址
int Head, N, K, p; //第一个节点地址,所有结点数,翻转子序列长度
int main() {
scanf("%d%d%d", &Head, &N, &K);
list[0] = Head;
for (int i = 0; i < N; ++i) {
int index, data, next;
scanf("%d%d%d", &index, &data, &next);
a[index].Data = data;
a[index].Next = next;
}
p = a[Head].Next;
int sum = 1;
while (p != -1) {
list[sum++] = p;
p = a[p].Next;
}
int j = 0;
while (j + K <= sum) { //双指针法
int left = j, right = j + K - 1;
while (left < right) {
int temp = list[left]; //操作的是记录地址的指针
list[left] = list[right];
list[right] = temp;
left++;
right--;
}
j = j + K;
}
for (int i = 0; i < sum - 1; ++i) {
int id = list[i]; //第i个结点的索引
printf("%05d %d %05d\n", id, a[id].Data, list[i + 1]); //直接打印交换后的下一个地址,原来记录在a里的下一位没用了
}
printf("%05d %d -1\n", list[sum - 1], a[list[sum - 1]].Data);
return 0;
}
/*这种思路桥面在用两个大数组,一个用来记录当前节点的数据域与下一个指针,另一个数组用来记录当前节点的地址
双指针法交换节点的时候其实交换当前节点的地址即可*/
链表+数组版
/*整体思路
用一个大数组用于保存原始数据,下标用于保存当前数据地址
创建一个链表,只保存当前节点地址,用于倒置,倒置后只用
通过链表保存的当前节点地址到结构体数组中访问对应的数据与下一个节点地址即可
*/
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100005
#define END -1
/*存放当前节点地址链表的数据结构,双向链表*/
typedef struct Node ListNode;
typedef ListNode* List;
struct Node {
List Previous;
int Ip;
List Next;
};
/*存放临时数据的结构体数组,下标表示当前地址*/
typedef struct Data Data;
struct Data {
int Data, NextIp;
};
Data Info[MAX_SIZE]; //这个是一个结构体数组用来存储一开始给的无序数据
/*读取*/
void Read(int nodeNum) { //这里命名为nodeNum是因为太坑了,还可能有节点不在链表上,题干说N只是节点数量
for(int i = 0; i < nodeNum; i++) {
int ip;
scanf("%d", &ip);
scanf("%d %d", &Info[ip].Data, &Info[ip].NextIp);
}
return;
}
/*尾插法*/
void Attach(int ip, List* rear) {
/*申请一个新的节点*/
(*rear)->Next = (List)malloc(sizeof(ListNode));
List previous = *rear;
(*rear) = (*rear)->Next;
/*填充这个新的节点*/
(*rear)->Previous = previous;
(*rear)->Ip = ip;
(*rear)->Next = NULL;
return;
}
/*顺出一个正常链表*/
List CreateList(int headAddress, int* listLength) {
int ptrInt = headAddress;
List dummyHead = (List)malloc(sizeof(ListNode));
List ptrList = dummyHead;
ptrList->Next = NULL;
while(ptrInt != END) {
int data = Info[ptrInt].Data;
int nextIp = Info[ptrInt].NextIp;
Attach(ptrInt, &ptrList);
ptrInt = Info[ptrInt].NextIp;
(*listLength)++;
}
return dummyHead;
}
/*打印链表*/
void PrintList(List L) {
List ptr = L->Next;
while(ptr) {
printf("%05d %d %05d", ptr->Ip, Info[ptr->Ip].Data, Info[ptr->Ip].NextIp);
if(ptr->Next) printf("\n");
ptr = ptr->Next;
}
return;
}
/*删除链表*/
void DeleteList(List L) {
List ptr = L->Next;
while(ptr) {
List tmp = ptr;
ptr = ptr->Next;
free(tmp);
}
return;
}
void Swap(List* node1, List* node2) {
int tmp = (*node1)->Ip;
(*node1)->Ip = (*node2)->Ip;
(*node2)->Ip = tmp;
return;
}
void ReverseList(List L, int listLength, int reverseLength) {
List ptrFront = L, ptrBack = L->Next;
int ptrInt = 0;
while(ptrInt + reverseLength <= listLength && ptrFront && ptrBack) { //循环终止条件,最后剩余片段不够倒置长度
for(int i = 0; i < reverseLength - 1; i++) {
ptrBack = ptrBack->Next;
}
List nextStart = ptrBack;
ptrFront = ptrFront->Next;
while(ptrFront->Previous != ptrBack && ptrFront != ptrBack ) {
Swap(&ptrFront, &ptrBack);
ptrFront = ptrFront->Next;
ptrBack = ptrBack->Previous;
}
ptrFront = nextStart;
ptrBack = nextStart->Next;
ptrInt += reverseLength;
}
List ret = L->Next;
while(ret) {
int ip = ret->Ip;
printf("%05d %d ", ip, Info[ip].Data, Info[ip].NextIp);
if(ret->Next != NULL) {
printf("%05d\n", ret->Next->Ip);
}
else printf("-1");
ret = ret->Next;
}
return;
}
int main() {
int headAddress, N, K;
scanf("%d %d %d", &headAddress, &N, &K);
Read(N); //读入原始数据
int listLength = 0;
List initialList = CreateList(headAddress, &listLength); //整理原始链表
ReverseList(initialList, listLength, K); //操作链表:倒置
DeleteList(initialList); //删除链表
return 0;
}