PAT 02-1. Reversing Linked List (25)
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 <script type="math/tex" id="MathJax-Element-1"><=10^5</script>) 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
分析:
第一行:00100 表示第一个节点的位置,即节点: 00100 1 12309 然后根据12309找到第二个节点:12309 2 33218,继续找,直到找到最后一个节点 68237 6 -1。
形成的单链表是 1 -> 2 -> 3 -> 4 -> 5 -> 6。 第一行第二个数N=6,代表接下来会输入6个节点,K=4,意思是每4个节点逆转,余下2个节点,不足4个,故不反转。输出 4->3->2->1->5->6,即:00000 4 33218 -> 33218 3 12309 -> 12309 2 00100 …。
需要注意的坑点:
- 1、注意指针的使用,如果K=1,链表不反转,K等于链表的节点数,链表整个反转,如果链表的节点数能被K整除,则每段都要反转。
- 2、输出的时候节点的Next 是和逆转后的节点相关,不是原先节点的Next;如:输入的时候节点 00000 4 99999 输出的时候应为 00000 4 33218,应为反转后节点4的下一个节点为3,而3的Address是33218。
3、输入可能不止一个链表, 有些节点不在要反转的那个链表上, 输出的时候应当忽略. 比如
00100 6 4 00000 4 99999 00100 1 12309 68237 6 -1 33218 3 -1 99999 5 68237 12309 2 33218
- 4、特殊坑点, 由于输入可能不止一个链表, 我们的K也有可能大于有效节点数。故此情况应将链表直接输出,无需逆转!
方法一:
使用链表存储节点,并通过链表操作返回逆转序列,使用C语言编写。优点是内存消耗少。
#include <stdio.h>
#include <malloc.h>
//#include <stdlib.h>
typedef struct tagLNode{
int addr; //节点位置
int data; //Data值
int nextAddr; //下个节点位置
struct tagLNode *next; //指向下个节点的指针
} LNode;
//反转单链表函数
LNode *listReverse(LNode *head,int k);
//返回节点addr值为参数nextAddr的节点的前一个节点的指针
LNode *listFind(LNode *p,int nextAddr);
//输出单链表 参数为单链表的头节点
void printList(LNode *a);
int main()
{
int firstAddr;
int n = 0; //节点数 N
int k =0; //反转子链表的长度K
int num = 0; //链表建好之后的链表节点数
int i = 0;
int j = 0;
LNode *head; //头指针
LNode *p; //用于移动链表指针
LNode *newp; //用于新建节点
LNode *temp;
scanf("%d %d %d",&firstAddr, &n, &k);
//输入节点
head = (LNode *)malloc(sizeof(LNode));
p = head;
p->nextAddr = firstAddr;
for(i = 0; i < n; i++){
newp = (LNode *)malloc(sizeof(LNode));
scanf("%d %d %d",&newp->addr, &newp->data, &newp->nextAddr);
p->next = newp;
p = newp;
}
newp->next = NULL; //最后一个元素
//从输入节点中构建单链表
p = head;
newp = NULL;
for(i = 0; i < n; i++){
newp = listFind(p,p->nextAddr);
if(newp != p){ //p->next不是我们此次查找的节点
temp = p->next;
p->next = newp->next;
newp->next = newp->next->next;
p->next->next = temp;
}
p=p->next;
if(p->nextAddr == -1){
p->next = NULL;
num = i+1; //链表的真实长度
break; //结束
}
}
//反转序列
p = head; //p指针指向链表的头节点
newp = NULL; //反转链表函数的返回值
if( k <= num ){
for(i = 0; i < (num / k); i++){
newp = listReverse(p,k); //反转后的链表的第一个节点(不是头结点)
p -> next = newp; // 第一次执行,a[0]->next 指向第一段子链表反转的第一个节点
p -> nextAddr = newp ->addr; // 更改Next值,指向逆转后它的下一个节点的位置
//使p指向下一段需要反转的子链表的头节点(第一个节点的前一个节点)
j=0;
while(j < k){
p = p -> next; //当k<<N时,需要返回多次
j++;
}
}
}
//序列输出
printList(head);
//system("PAUSE");
return 0;
}
/*
LNode *listFind(LNode *p,int nextAddr):
查找节点addr值为参数nextAddr的节点
参数1:链表查找的起始位置
参数2:查找的节点的addr值
返回值:返回查找到节点的前一个节点的指针
*/
LNode *listFind(LNode *p,int nextAddr)
{
LNode *temp = p ;
while(temp->next->next != NULL){
if(temp->next->addr == nextAddr){
break;
}
temp = temp ->next;
}
return temp;
}
/*
LNode *listReverse(LNode *head,int k):
反转单链表函数
参数1:单链表的头节点
参数2:反转子链表的长度
返回值:反转后的链表的第一个节点(不是头节点)
*/
LNode *listReverse(LNode *head,int k)
{
int count = 1;
LNode *front = head ->next;
LNode *rear = front->next;
LNode *temp = NULL;
while(count < k){
temp = rear -> next;
rear ->next = front;
rear ->nextAddr = front->addr;
front = rear ; //newl向后走一个节点
rear = temp ; //temp向后走一个节点
count++;
}
//使反转后的最后一个节点指向下一段子链表的第一个节点
head->next->next = rear;
if (rear != NULL){
//修改Next值,使它指向下一个节点的位置
head->next->nextAddr = rear->addr;
}else{
//如果old为NULL,即没有下一个子链表,那么反转后的最后一个节点即是真个链表的最后一个节点
head->next->nextAddr = -1;
}
return front;
}
//序列输出
void printList(LNode *a)
{
LNode *p = a;
while (p->next != NULL){
p = p->next;
if (p->nextAddr != -1){
//格式输出,%.5意味着如果一个整数不足5位,输出时前面补0 如:22,输出:00022
printf("%.5d %d %.5d\n", p->addr, p->data, p->nextAddr);
}else{
//-1不需要以%.5格式输出
printf("%.5d %d %d\n", p->addr, p->data, p->nextAddr);
}
}
}
方法二:
方法二与方法一相比,不同处在于节点的存储方式是使用数组,但此方式占用较大内存。建议参考使用方法一。
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define MAX_SIZE 100004
typedef struct tagLNode{
int addr; //节点位置
int data; //Data值
int nextAddr; //下个节点位置
struct tagLNode *next; //指向下个节点的指针
} LNode;
/*
LNode *listReverse(LNode *head,int k);
反转单链表函数
参数1:单链表的头节点
参数2:反转子链表的长度
返回值:反转后的链表的第一个节点(不是头节点)
*/
LNode *listReverse(LNode *head,int k);
//输出单链表 参数为单链表的头节点
void printList(LNode *a);
int main()
{
int firstAddr;
int n = 0; //节点数 N
int k =0; //反转子链表的长度K
int num = 0; //链表建好之后的链表节点数
int data[MAX_SIZE]; //存data值 节点位置作为索引值
int next[MAX_SIZE]; //存next值 节点位置为索引
int temp; //临时变量,输入的时候用
int i = 0;
int j = 0;
LNode *head; //头指针
LNode *p; //用于移动链表指针
LNode *newp; //用于新建节点
scanf("%d %d %d",&firstAddr, &n, &k);
//读输入的节点
for(i =0; i<n; i++){
scanf("%d",&temp);
scanf("%d %d",&data[temp],&next[temp]);
}
//构建单链表
head = (LNode *)malloc(sizeof(LNode));
p = head;
p->nextAddr = firstAddr;
for(i = 0; i < n; i++){
newp = (LNode *)malloc(sizeof(LNode));
p->next = newp;
temp = p->nextAddr;
newp->addr = temp;
newp->data = data[temp];
newp->nextAddr = next[temp];
p = newp;
if(p->nextAddr == -1){
p->next = NULL;
num = i+1; //链表的真实长度
break; //结束
}
}
p = head; //p指针指向链表的头节点
newp = NULL; //反转链表函数的返回值
if( k <= num ){
for(i = 0; i < (num / k); i++){
newp = listReverse(p,k); //反转后的链表的第一个节点(不是头结点)
p -> next = newp; // 第一次执行,a[0]->next 指向第一段子链表反转的第一个节点
p -> nextAddr = newp ->addr; // 更改Next值,指向逆转后它的下一个节点的位置
//使p指向下一段需要反转的子链表的头节点(第一个节点的前一个节点)
j=0;
while(j < k){
p = p -> next;
j++;
}
}
}
printList(head);
system("PAUSE");
return 0;
}
LNode *listReverse(LNode *head,int k)
{
int count = 1;
LNode *front = head ->next;
LNode *rear = front->next;
LNode *temp = NULL;
while(count < k){
temp = rear -> next;
rear ->next = front;
rear ->nextAddr = front->addr;
front = rear ; //newl向后走一个节点
rear = temp ; //temp向后走一个节点
count++;
}
//使反转后的最后一个节点指向下一段子链表的第一个节点
head->next->next = rear;
if (rear != NULL){
//修改Next值,使它指向下一个节点的位置
head->next->nextAddr = rear->addr;
}else{
//如果old为NULL,即没有下一个子链表,那么反转后的最后一个节点即是真个链表的最后一个节点
head->next->nextAddr = -1;
}
return front;
}
void printList(LNode *a)
{
LNode *p = a;
while (p->next != NULL){
p = p->next;
if (p->nextAddr != -1){
//格式输出,%.5意味着如果一个整数不足5位,输出时前面补0 如:22,输出:00022
printf("%.5d %d %.5d\n", p->addr, p->data, p->nextAddr);
}else{
//-1不需要以%.5格式输出
printf("%.5d %d %d\n", p->addr, p->data, p->nextAddr);
}
}
}
@声明:本文部分内容参考kevin-lwb和Sigmainfy 烟客旅人的博客。其博客地址分别为:
kevin-lwb:www.cnblogs.com/kevin-lwb/p/4283456.html
Sigmainfy 烟客旅人:ech-wonderland.net/blog/pat-1074-reversing-linked-list.html