PAT 02-1. Reversing Linked List (25)

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-lwbSigmainfy 烟客旅人的博客。其博客地址分别为:
kevin-lwb:www.cnblogs.com/kevin-lwb/p/4283456.html
Sigmainfy 烟客旅人:ech-wonderland.net/blog/pat-1074-reversing-linked-list.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值