首页 > 编程语言 > 详细

PTA basic 1025 反转链表 (25 分) c语言实现(gcc)

时间:2021-04-24 00:55:12      阅读:23      评论:0      收藏:0      [点我收藏+]

给定一个常数 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 (≤)、以及正整数 K (≤),即要求反转的子链结点的个数。结点的地址是 5 位非负整数,NULL 地址用 ? 表示。

接下来有 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

核心思路图解

 

技术分享图片

 

技术分享图片

 

 技术分享图片

 

技术分享图片

 

 

 

 

 




  1 //初步思路声明链表,然后按照头插法插入结点,当index+k<=n的时候,原地翻转index到index+k-1的结点,temp指针指向index的指针指向
  2 
  3 //提交后检测点5超时,显然是给出了极限条件,即当数据足够多的时候,遍历算法的效率急速下降,所要用到链表和指针来解决问题
  4 
  5 //遍历的方法会超时,用静态结构数组存储数据,使用下标作为指针模拟链表,进行链表的局部翻转,然后输出翻转后的结果
  6 
  7 //关于局部翻转的算法
  8 //核心思想
  9 //首先用三个指针来记录翻转前一个结点地址pre,后一个结点地址next以及当前节点地址current,然后就是处理子链首尾的连接
 10 //因为翻转后链表的首地址会发生变化,因此需要单独记录链表的首地址,这里用一次单独的循环来记录,结束后产生的子链首地址就是链表的首地址
 11 //题目给出的测试用例中,如果剩余子链不够k个,就不需要翻转,所以还要考虑到剩余结点的连接
 12 
 13 //第一次提交测试034通过,百度看别人写的代码 1.所给数据可能存在无效结点(测试点6通过)
 14 
 15 //第二次提交 使用静态链表方式重写了代码,测试点0246通过
 16 
 17 //第三次提交 制作了图解的链表算法过程 并且修正了多个子链表翻转时候的边界条件,检测点023456通过
 18 
 19 //最后一次提交通过
 20 
 21 //检测点1正好有N=m*K个子串需要翻转
 22 
 23 //检测点0 2 4 6 只有有1个子串,且N>K需要翻转的时候
 24 
 25 //检测点 3 5 有m个子串需要翻转且N>m*K
 26 
 27 //测试用例1
 28 //00100 12 4
 29 //00000 4 99999
 30 //00100 1 12309
 31 //68237 6 34257
 32 //33218 3 00000
 33 //99999 5 68237
 34 //12309 2 33218
 35 //34257 7 12922
 36 //12922 8 98122
 37 //98122 9 13812
 38 //13812 10 23721
 39 //23721 11 12371
 40 //12371 12 -1
 41 
 42 //测试用例2
 43 //00100 12 4
 44 //00000 4 99999
 45 //00100 1 12309
 46 //68237 6 34257
 47 //33218 3 00000
 48 //99999 5 68237
 49 //12309 2 33218
 50 //34257 7 12922
 51 //12922 8 98122
 52 //98122 9 13812
 53 //13812 10 23721
 54 //23721 11 12371
 55 //12371 12 18271
 56 //18271 13 -1
 57 
 58 
 59 #include "stdio.h"
 60 #include "stdlib.h"
 61 #include "string.h"
 62 
 63 typedef struct protoTypeNode{
 64     //地址直接用数组下标存储 ,使用的是静态结构体数组,原理和链表一样,但是题目已经给定了数据的地址,没办法使用动态链表来存储固定地址尤其是地址为0的数据,
 65     int data;
 66     int next;
 67 } node;
 68 
 69 //翻转链表核心算法
 70 int reverse(node *linkListPointer,int head,int n,int k){
 71     node *lp=linkListPointer;//链表指针
 72     int pre=0,current=0,next=0,nexthead,j=0,rear=0,repeatTimes,i;
 73     
 74     //pre用于记录需要翻转的结点的前一个结点地址
 75     //current当前翻转结点的地址
 76     //next下一个需要翻转结点的地址
 77     //nexthead下一个子链的首结点
 78     //rear子链翻转后的尾结点(即未翻转的子链的头结点)
 79     //repeatTime有多少个子链需要翻转
 80     //remainList需要翻转的子链全部翻转后是否还剩下结点
 81     
 82     pre=head;//指向未翻转的子链表的头结点
 83     current=lp[head].next;//指向头结点的下一跳结点,同时也是准备翻转的结点,准备用于翻转指向原局部头结点
 84     next=lp[current].next;//保存翻转结点的下一跳结点
 85     for(i=0;i<k-1;i++){//第一个元素的下一跳指针需要单独处理,所以重复k-1次
 86         lp[current].next=pre;//前一个元素的地址pre存入current指向的结点的下一跳指针中
 87         pre=current;//pre current next前移一位
 88         current=next;
 89         next=lp[next].next;
 90     }
 91     rear=head;//原局部头结点成为翻转后的尾结点
 92     head=pre;//局部翻转后,生成新的链表表头结点
 93     //翻转剩下的子链
 94     repeatTimes=n/k;//需要翻转多少个子链
 95     for(j=0;j<repeatTimes-1;j++){
 96         nexthead=pre=current;//更新子链头结点 //pre current next前移一位
 97         current=lp[nexthead].next;
 98         next=lp[current].next;
 99         for(i=0;i<k-1;i++){
100             lp[current].next=pre;//将前一个结点的地址存入当前翻转节点的next指针中
101             pre=current;//pre current next前移一位
102             current=next;
103             next=lp[next].next;
104         }
105         lp[rear].next=pre;//使 上一个翻转子链表的尾结点的指针指向当前 翻转子链表的新头结点
106         rear=nexthead;//当前子链表的局部头结点成为翻转后的尾结点
107     }
108     lp[rear].next=current;//翻转后的子链尾结点指向剩余子链的首结点
109     return head;
110 }
111 
112 //统计有效结点
113 int countList(node *lp,int head){
114     int count=0,current=head;
115     while(lp[current].next!=-1){
116         current=lp[current].next;
117         count++;
118     }
119     return count+1;
120 }
121 
122 //按序打印链表
123 void printList(node *lp,int head,int count){
124     int current=head,i;
125     //    printf("\n");
126     for(i=0;i<count-1;i++){
127         printf("%05d %d %05d\n",current,lp[current].data,lp[current].next);
128         current=lp[current].next;
129     }
130     printf("%05d %d %d\n",current,lp[current].data,lp[current].next);
131 }
132 
133 int main(){
134     int head=0,i,n=0,k=0,count,address,data,next,newhead;
135     scanf("%d %d %d",&head,&n,&k);
136     node linkList[100000];//N<=10^5
137     for(i=0;i<n;i++){
138         scanf("%d %d %d",&address,&data,&next);
139         linkList[address].data=data;
140         linkList[address].next=next;
141     }
142     count=countList(linkList,head);
143     newhead=reverse(linkList, head, count, k);
144     printList(linkList, newhead, count);
145     
146     return 0;
147 }

 



技术分享图片

 

 

 

后记.
说实话,以前只是知道链表的逻辑,但是手动写代码出来建立一个链表来实现某种算法一直都不会,花了挺长时间一步步debug才搞懂
其实翻转子链的地方并不难理解, 难点是边界处理子链的链接问题
包括 链表的首地址更新,上一个子链的尾结点和下一个子链的头结点的链接, 更新头尾结点

 

PTA basic 1025 反转链表 (25 分) c语言实现(gcc)

原文:https://www.cnblogs.com/ichiha/p/14695742.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!